diff options
Diffstat (limited to 'Source/WebCore/platform/network')
197 files changed, 28998 insertions, 0 deletions
diff --git a/Source/WebCore/platform/network/AuthenticationChallengeBase.cpp b/Source/WebCore/platform/network/AuthenticationChallengeBase.cpp new file mode 100644 index 0000000..df80441 --- /dev/null +++ b/Source/WebCore/platform/network/AuthenticationChallengeBase.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 "AuthenticationChallenge.h" + +#include "ResourceHandle.h" + +namespace WebCore { + +AuthenticationChallengeBase::AuthenticationChallengeBase() + : m_isNull(true) + , m_previousFailureCount(0) +{ +} + +AuthenticationChallengeBase::AuthenticationChallengeBase(const ProtectionSpace& protectionSpace, + const Credential& proposedCredential, + unsigned previousFailureCount, + const ResourceResponse& response, + const ResourceError& error) + : m_isNull(false) + , m_protectionSpace(protectionSpace) + , m_proposedCredential(proposedCredential) + , m_previousFailureCount(previousFailureCount) + , m_failureResponse(response) + , m_error(error) +{ +} + +unsigned AuthenticationChallengeBase::previousFailureCount() const +{ + return m_previousFailureCount; +} + +const Credential& AuthenticationChallengeBase::proposedCredential() const +{ + return m_proposedCredential; +} + +const ProtectionSpace& AuthenticationChallengeBase::protectionSpace() const +{ + return m_protectionSpace; +} + +const ResourceResponse& AuthenticationChallengeBase::failureResponse() const +{ + return m_failureResponse; +} + +const ResourceError& AuthenticationChallengeBase::error() const +{ + return m_error; +} + +bool AuthenticationChallengeBase::isNull() const +{ + return m_isNull; +} + +void AuthenticationChallengeBase::nullify() +{ + m_isNull = true; +} + +bool AuthenticationChallengeBase::compare(const AuthenticationChallenge& a, const AuthenticationChallenge& b) +{ + if (a.isNull() && b.isNull()) + return true; + + if (a.isNull() || b.isNull()) + return false; + + if (a.protectionSpace() != b.protectionSpace()) + return false; + + if (a.proposedCredential() != b.proposedCredential()) + return false; + + if (a.previousFailureCount() != b.previousFailureCount()) + return false; + + if (a.failureResponse() != b.failureResponse()) + return false; + + if (a.error() != b.error()) + return false; + + return AuthenticationChallenge::platformCompare(a, b); +} + +} diff --git a/Source/WebCore/platform/network/AuthenticationChallengeBase.h b/Source/WebCore/platform/network/AuthenticationChallengeBase.h new file mode 100644 index 0000000..9d85866 --- /dev/null +++ b/Source/WebCore/platform/network/AuthenticationChallengeBase.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 AuthenticationChallengeBase_h +#define AuthenticationChallengeBase_h + +#include "Credential.h" +#include "ProtectionSpace.h" +#include "ResourceResponse.h" +#include "ResourceError.h" + +namespace WebCore { + +class AuthenticationChallenge; + +class AuthenticationChallengeBase { +public: + AuthenticationChallengeBase(); + AuthenticationChallengeBase(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error); + + unsigned previousFailureCount() const; + const Credential& proposedCredential() const; + const ProtectionSpace& protectionSpace() const; + const ResourceResponse& failureResponse() const; + const ResourceError& error() const; + + bool isNull() const; + void nullify(); + + static bool compare(const AuthenticationChallenge& a, const AuthenticationChallenge& b); + +protected: + // The AuthenticationChallenge subclass may "shadow" this method to compare platform specific fields + static bool platformCompare(const AuthenticationChallengeBase&, const AuthenticationChallengeBase&) { return true; } + + bool m_isNull; + ProtectionSpace m_protectionSpace; + Credential m_proposedCredential; + unsigned m_previousFailureCount; + ResourceResponse m_failureResponse; + ResourceError m_error; +}; + +inline bool operator==(const AuthenticationChallenge& a, const AuthenticationChallenge& b) { return AuthenticationChallengeBase::compare(a, b); } +inline bool operator!=(const AuthenticationChallenge& a, const AuthenticationChallenge& b) { return !(a == b); } + +} + +#endif diff --git a/Source/WebCore/platform/network/AuthenticationClient.h b/Source/WebCore/platform/network/AuthenticationClient.h new file mode 100644 index 0000000..1e17910 --- /dev/null +++ b/Source/WebCore/platform/network/AuthenticationClient.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2009 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 AuthenticationClient_h +#define AuthenticationClient_h + +namespace WebCore { + +class AuthenticationChallenge; +class Credential; + +class AuthenticationClient { +public: + virtual void receivedCredential(const AuthenticationChallenge&, const Credential&) = 0; + virtual void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) = 0; + virtual void receivedCancellation(const AuthenticationChallenge&) = 0; + + void ref() { refAuthenticationClient(); } + void deref() { derefAuthenticationClient(); } + +protected: + virtual ~AuthenticationClient() { } + +private: + virtual void refAuthenticationClient() = 0; + virtual void derefAuthenticationClient() = 0; +}; + +} + +#endif diff --git a/Source/WebCore/platform/network/BlobData.cpp b/Source/WebCore/platform/network/BlobData.cpp new file mode 100644 index 0000000..ff39ecc --- /dev/null +++ b/Source/WebCore/platform/network/BlobData.cpp @@ -0,0 +1,98 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "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; + +RawData::RawData() +{ +} + +void RawData::detachFromCurrentThread() +{ +} + +void BlobDataItem::detachFromCurrentThread() +{ + data->detachFromCurrentThread(); + path = path.crossThreadString(); + url = url.copy(); +} + +PassOwnPtr<BlobData> BlobData::create() +{ + return adoptPtr(new BlobData()); +} + +void BlobData::detachFromCurrentThread() +{ + 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(PassRefPtr<RawData> data, long long offset, long long length) +{ + m_items.append(BlobDataItem(data, offset, length)); +} + +void BlobData::appendFile(const String& path) +{ + m_items.append(BlobDataItem(path)); +} + +void BlobData::appendFile(const String& path, long long offset, long long length, double expectedModificationTime) +{ + m_items.append(BlobDataItem(path, offset, length, expectedModificationTime)); +} + +void BlobData::appendBlob(const KURL& url, long long offset, long long length) +{ + m_items.append(BlobDataItem(url, offset, length)); +} + +void BlobData::swapItems(BlobDataItemList& items) +{ + m_items.swap(items); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/BlobData.h b/Source/WebCore/platform/network/BlobData.h new file mode 100644 index 0000000..1ff6344 --- /dev/null +++ b/Source/WebCore/platform/network/BlobData.h @@ -0,0 +1,184 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 BlobData_h +#define BlobData_h + +#include "KURL.h" +#include "PlatformString.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; + + // Default constructor. + BlobDataItem() + : type(Data) + , offset(0) + , length(toEndOfFile) + , expectedModificationTime(doNotCheckFileChange) + { + } + + // Constructor for String type (complete string). + explicit BlobDataItem(PassRefPtr<RawData> data) + : type(Data) + , data(data) + , offset(0) + , length(toEndOfFile) + , expectedModificationTime(doNotCheckFileChange) + { + } + + // Constructor for File type (complete file). + explicit BlobDataItem(const String& path) + : type(File) + , path(path) + , offset(0) + , length(toEndOfFile) + , expectedModificationTime(doNotCheckFileChange) + { + } + + // Constructor for File type (partial file). + BlobDataItem(const String& path, long long offset, long long length, double expectedModificationTime) + : type(File) + , path(path) + , offset(offset) + , length(length) + , expectedModificationTime(expectedModificationTime) + { + } + + // Constructor for Blob type. + BlobDataItem(const KURL& url, long long offset, long long length) + : type(Blob) + , url(url) + , offset(offset) + , length(length) + , expectedModificationTime(doNotCheckFileChange) + { + } + + // Detaches from current thread so that it can be passed to another thread. + void detachFromCurrentThread(); + + enum { Data, File, Blob } type; + + // For Data type. + RefPtr<RawData> data; + + // For File type. + String path; + + // For Blob type. + KURL url; + + 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(); + + // 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; } + + const String& contentDisposition() const { return m_contentDisposition; } + void setContentDisposition(const String& contentDisposition) { m_contentDisposition = contentDisposition; } + + const BlobDataItemList& items() const { return m_items; } + void swapItems(BlobDataItemList&); + + 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); + +private: + friend class BlobRegistryImpl; + friend class BlobStorageData; + + BlobData() { } + + // This is only exposed to BlobStorageData. + void appendData(const RawData&, long long offset, long long length); + + String m_contentType; + String m_contentDisposition; + BlobDataItemList m_items; +}; + +} // namespace WebCore + +#endif // BlobData_h diff --git a/Source/WebCore/platform/network/BlobRegistry.h b/Source/WebCore/platform/network/BlobRegistry.h new file mode 100644 index 0000000..7e64233 --- /dev/null +++ b/Source/WebCore/platform/network/BlobRegistry.h @@ -0,0 +1,71 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 BlobRegistry_h +#define BlobRegistry_h + +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class BlobData; +class BlobRegistry; +class KURL; +class ResourceError; +class ResourceHandle; +class ResourceHandleClient; +class ResourceRequest; +class ResourceResponse; + +// Returns a single instance of BlobRegistry. +BlobRegistry& blobRegistry(); + +// BlobRegistry is not thread-safe. It should only be called from main thread. +class BlobRegistry { +public: + // Registers a blob URL referring to the specified blob data. + virtual void registerBlobURL(const KURL&, PassOwnPtr<BlobData>) = 0; + + // Registers a blob URL referring to the blob data identified by the specified srcURL. + virtual void registerBlobURL(const KURL&, const KURL& srcURL) = 0; + + virtual void unregisterBlobURL(const KURL&) = 0; + virtual PassRefPtr<ResourceHandle> createResourceHandle(const ResourceRequest&, ResourceHandleClient*) = 0; + virtual bool loadResourceSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data) = 0; + +protected: + virtual ~BlobRegistry() { } +}; + +} // namespace WebCore + +#endif // BlobRegistry_h diff --git a/Source/WebCore/platform/network/BlobRegistryImpl.cpp b/Source/WebCore/platform/network/BlobRegistryImpl.cpp new file mode 100644 index 0000000..2c4e8fa --- /dev/null +++ b/Source/WebCore/platform/network/BlobRegistryImpl.cpp @@ -0,0 +1,178 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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" + +#if ENABLE(BLOB) + +#include "BlobRegistryImpl.h" + +#include "BlobResourceHandle.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceLoader.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include <wtf/MainThread.h> +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +#if !PLATFORM(CHROMIUM) +BlobRegistry& blobRegistry() +{ + ASSERT(isMainThread()); + DEFINE_STATIC_LOCAL(BlobRegistryImpl, instance, ()); + return instance; +} +#endif + +bool BlobRegistryImpl::shouldLoadResource(const ResourceRequest& request) const +{ + // If the resource is not fetched using the GET method, bail out. + if (!equalIgnoringCase(request.httpMethod(), "GET")) + return false; + + return true; +} + +PassRefPtr<ResourceHandle> BlobRegistryImpl::createResourceHandle(const ResourceRequest& request, ResourceHandleClient* client) +{ + if (!shouldLoadResource(request)) + return 0; + + return BlobResourceHandle::create(m_blobs.get(request.url().string()), request, client); +} + +bool BlobRegistryImpl::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) +{ + if (!shouldLoadResource(request)) + return false; + + BlobResourceHandle::loadResourceSynchronously(m_blobs.get(request.url().string()), request, error, response, data); + return true; +} + +void BlobRegistryImpl::appendStorageItems(BlobStorageData* blobStorageData, const BlobDataItemList& items) +{ + for (BlobDataItemList::const_iterator iter = items.begin(); iter != items.end(); ++iter) { + if (iter->type == BlobDataItem::Data) + blobStorageData->m_data.appendData(iter->data, iter->offset, iter->length); + else { + ASSERT(iter->type == BlobDataItem::File); + blobStorageData->m_data.appendFile(iter->path, iter->offset, iter->length, iter->expectedModificationTime); + } + } +} + +void BlobRegistryImpl::appendStorageItems(BlobStorageData* blobStorageData, const BlobDataItemList& items, long long offset, long long length) +{ + ASSERT(length != BlobDataItem::toEndOfFile); + + BlobDataItemList::const_iterator iter = items.begin(); + if (offset) { + for (; iter != items.end(); ++iter) { + if (offset >= iter->length) + offset -= iter->length; + else + break; + } + } + + for (; iter != items.end() && length > 0; ++iter) { + long long currentLength = iter->length - offset; + long long newLength = currentLength > length ? length : currentLength; + if (iter->type == BlobDataItem::Data) + blobStorageData->m_data.appendData(iter->data, iter->offset + offset, newLength); + else { + ASSERT(iter->type == BlobDataItem::File); + blobStorageData->m_data.appendFile(iter->path, iter->offset + offset, newLength, iter->expectedModificationTime); + } + length -= newLength; + offset = 0; + } +} + +void BlobRegistryImpl::registerBlobURL(const KURL& url, PassOwnPtr<BlobData> blobData) +{ + ASSERT(isMainThread()); + + RefPtr<BlobStorageData> blobStorageData = BlobStorageData::create(blobData->contentType(), blobData->contentDisposition()); + + // The blob data is stored in the "canonical" way. That is, it only contains a list of Data and File items. + // 1) The Data item is denoted by the raw data and the range. + // 2) The File item is denoted by the file path, the range and the expected modification time. + // All the Blob items in the passing blob data are resolved and expanded into a set of Data and File items. + + 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()); + break; + case BlobDataItem::File: + blobStorageData->m_data.appendFile(iter->path, iter->offset, iter->length, iter->expectedModificationTime); + break; + case BlobDataItem::Blob: + if (m_blobs.contains(iter->url.string())) + appendStorageItems(blobStorageData.get(), m_blobs.get(iter->url.string())->items(), iter->offset, iter->length); + break; + } + } + + m_blobs.set(url.string(), blobStorageData); +} + +void BlobRegistryImpl::registerBlobURL(const KURL& url, const KURL& srcURL) +{ + ASSERT(isMainThread()); + + RefPtr<BlobStorageData> src = m_blobs.get(srcURL.string()); + ASSERT(src); + if (!src) + return; + + m_blobs.set(url.string(), src); +} + +void BlobRegistryImpl::unregisterBlobURL(const KURL& url) +{ + ASSERT(isMainThread()); + m_blobs.remove(url.string()); +} + +PassRefPtr<BlobStorageData> BlobRegistryImpl::getBlobDataFromURL(const KURL& url) const +{ + ASSERT(isMainThread()); + return m_blobs.get(url.string()); +} + +} // namespace WebCore + +#endif // ENABLE(BLOB) diff --git a/Source/WebCore/platform/network/BlobRegistryImpl.h b/Source/WebCore/platform/network/BlobRegistryImpl.h new file mode 100644 index 0000000..f616664 --- /dev/null +++ b/Source/WebCore/platform/network/BlobRegistryImpl.h @@ -0,0 +1,74 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 BlobRegistryImpl_h +#define BlobRegistryImpl_h + +#include "BlobData.h" +#include "BlobRegistry.h" +#include "BlobStorageData.h" +#include "PlatformString.h" +#include <wtf/HashMap.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +class KURL; +class ResourceError; +class ResourceHandle; +class ResourceHandleClient; +class ResourceRequest; +class ResourceResponse; + +// BlobRegistryImpl is not thread-safe. It should only be called from main thread. +class BlobRegistryImpl : public BlobRegistry { +public: + virtual ~BlobRegistryImpl() { } + + virtual void registerBlobURL(const KURL&, PassOwnPtr<BlobData>); + virtual void registerBlobURL(const KURL&, const KURL& srcURL); + virtual void unregisterBlobURL(const KURL&); + virtual PassRefPtr<ResourceHandle> createResourceHandle(const ResourceRequest&, ResourceHandleClient*); + virtual bool loadResourceSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data); + + PassRefPtr<BlobStorageData> getBlobDataFromURL(const KURL&) const; + +private: + bool shouldLoadResource(const ResourceRequest& request) const; + void appendStorageItems(BlobStorageData*, const BlobDataItemList&); + void appendStorageItems(BlobStorageData*, const BlobDataItemList&, long long offset, long long length); + + HashMap<String, RefPtr<BlobStorageData> > m_blobs; +}; + +} // namespace WebCore + +#endif // BlobRegistryImpl_h diff --git a/Source/WebCore/platform/network/BlobResourceHandle.cpp b/Source/WebCore/platform/network/BlobResourceHandle.cpp new file mode 100644 index 0000000..753052a --- /dev/null +++ b/Source/WebCore/platform/network/BlobResourceHandle.cpp @@ -0,0 +1,590 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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" + +#if ENABLE(BLOB) + +#include "BlobResourceHandle.h" + +#include "AsyncFileStream.h" +#include "BlobRegistryImpl.h" +#include "FileStream.h" +#include "FileSystem.h" +#include "HTTPParsers.h" +#include "KURL.h" +#include "ResourceError.h" +#include "ResourceLoader.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" + +namespace WebCore { + +static const unsigned bufferSize = 1024; +static const int maxVectorLength = 0x7fffffff; +static const long long positionNotSpecified = -1; + +static const int httpOK = 200; +static const int httpPartialContent = 206; +static const int httpNotAllowed = 403; +static const int httpNotFound = 404; +static const int httpRequestedRangeNotSatisfiable = 416; +static const int httpInternalError = 500; +static const char* httpOKText = "OK"; +static const char* httpPartialContentText = "Partial Content"; +static const char* httpNotAllowedText = "Not Allowed"; +static const char* httpNotFoundText = "Not Found"; +static const char* httpRequestedRangeNotSatisfiableText = "Requested Range Not Satisfiable"; +static const char* httpInternalErrorText = "Internal Server Error"; + +static const int notFoundError = 1; +static const int securityError = 2; +static const int rangeError = 3; +static const int notReadableError = 4; + +/////////////////////////////////////////////////////////////////////////////// +// BlobResourceSynchronousLoader + +namespace { + +class BlobResourceSynchronousLoader : public ResourceHandleClient { +public: + BlobResourceSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&); + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int /*lengthReceived*/); + virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/); + virtual void didFail(ResourceHandle*, const ResourceError&); + +private: + ResourceError& m_error; + ResourceResponse& m_response; + Vector<char>& m_data; +}; + +BlobResourceSynchronousLoader::BlobResourceSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data) + : m_error(error) + , m_response(response) + , m_data(data) +{ +} + +void BlobResourceSynchronousLoader::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) +{ + // We cannot handle the size that is more than maximum integer. + const int intMaxForLength = 0x7fffffff; + if (response.expectedContentLength() > intMaxForLength) { + m_error = ResourceError(String(), notReadableError, response.url(), String()); + return; + } + + m_response = response; + + // Read all the data. + m_data.resize(static_cast<size_t>(response.expectedContentLength())); + static_cast<BlobResourceHandle*>(handle)->readSync(m_data.data(), static_cast<int>(m_data.size())); +} + +void BlobResourceSynchronousLoader::didReceiveData(ResourceHandle*, const char*, int, int) +{ +} + +void BlobResourceSynchronousLoader::didFinishLoading(ResourceHandle*, double) +{ +} + +void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error) +{ + m_error = error; +} + +} + +/////////////////////////////////////////////////////////////////////////////// +// BlobResourceHandle + +// static +void BlobResourceHandle::loadResourceSynchronously(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) +{ + BlobResourceSynchronousLoader loader(error, response, data); + RefPtr<BlobResourceHandle> handle = BlobResourceHandle::create(blobData, request, &loader, false); + handle->start(); +} + +static void delayedStart(void* context) +{ + static_cast<BlobResourceHandle*>(context)->start(); +} + +BlobResourceHandle::BlobResourceHandle(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async) + : ResourceHandle(request, client, false, false) + , m_blobData(blobData) + , m_async(async) + , m_errorCode(0) + , m_aborted(false) + , m_rangeOffset(positionNotSpecified) + , m_rangeEnd(positionNotSpecified) + , m_rangeSuffixLength(positionNotSpecified) + , m_totalRemainingSize(0) + , m_currentItemReadSize(0) + , m_sizeItemCount(0) + , m_readItemCount(0) + , m_fileOpened(false) +{ + if (m_async) { + // We need to take a ref. + m_asyncStream = client->createAsyncFileStream(this); + callOnMainThread(delayedStart, this); + } else + m_stream = FileStream::create(); +} + +BlobResourceHandle::~BlobResourceHandle() +{ + if (m_async) { + if (m_asyncStream) + m_asyncStream->stop(); + } else { + if (m_stream) + m_stream->stop(); + } +} + +void BlobResourceHandle::cancel() +{ + if (m_async) { + if (m_asyncStream) { + m_asyncStream->stop(); + m_asyncStream = 0; + } + } + + m_aborted = true; +} + +void BlobResourceHandle::start() +{ + // Do not continue if the request is aborted or an error occurs. + if (m_aborted || m_errorCode) + return; + + // If the blob data is not found, fail now. + if (!m_blobData) { + m_errorCode = notFoundError; + notifyResponse(); + return; + } + + // Parse the "Range" header we care about. + String range = firstRequest().httpHeaderField("Range"); + if (!range.isEmpty() && !parseRange(range, m_rangeOffset, m_rangeEnd, m_rangeSuffixLength)) { + m_errorCode = rangeError; + notifyResponse(); + return; + } + + if (m_async) + getSizeForNext(); + else { + for (size_t i = 0; i < m_blobData->items().size() && !m_aborted && !m_errorCode; ++i) + getSizeForNext(); + notifyResponse(); + } +} + +void BlobResourceHandle::getSizeForNext() +{ + // Do we finish validating and counting size for all items? + if (m_sizeItemCount >= m_blobData->items().size()) { + seek(); + + // Start reading if in asynchronous mode. + if (m_async) { + notifyResponse(); + m_buffer.resize(bufferSize); + readAsync(); + } + return; + } + + const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount); + switch (item.type) { + case BlobDataItem::Data: + didGetSize(item.length); + break; + case BlobDataItem::File: + if (m_async) + m_asyncStream->getSize(item.path, item.expectedModificationTime); + else + didGetSize(m_stream->getSize(item.path, item.expectedModificationTime)); + break; + default: + ASSERT_NOT_REACHED(); + } +} + +void BlobResourceHandle::didGetSize(long long size) +{ + // Do not continue if the request is aborted or an error occurs. + if (m_aborted || m_errorCode) + return; + + // If the size is -1, it means the file has been moved or changed. Fail now. + if (size == -1) { + m_errorCode = notFoundError; + notifyResponse(); + return; + } + + // The size passed back is the size of the whole file. If the underlying item is a sliced file, we need to use the slice length. + const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount); + if (item.type == BlobDataItem::File && item.length != BlobDataItem::toEndOfFile) + size = item.length; + + // Cache the size. + m_itemLengthList.append(size); + + // Count the size. + m_totalRemainingSize += size; + m_sizeItemCount++; + + // Continue with the next item. + getSizeForNext(); +} + +void BlobResourceHandle::seek() +{ + // Convert from the suffix length to the range. + if (m_rangeSuffixLength != positionNotSpecified) { + m_rangeOffset = m_totalRemainingSize - m_rangeSuffixLength; + m_rangeEnd = m_rangeOffset + m_rangeSuffixLength - 1; + } + + // Bail out if the range is not provided. + if (m_rangeOffset == positionNotSpecified) + return; + + // Skip the initial items that are not in the range. + long long offset = m_rangeOffset; + for (m_readItemCount = 0; m_readItemCount < m_blobData->items().size() && offset >= m_itemLengthList[m_readItemCount]; ++m_readItemCount) + offset -= m_itemLengthList[m_readItemCount]; + + // Set the offset that need to jump to for the first item in the range. + m_currentItemReadSize = offset; + + // Adjust the total remaining size in order not to go beyond the range. + if (m_rangeEnd != positionNotSpecified) { + long long rangeSize = m_rangeEnd - m_rangeOffset + 1; + if (m_totalRemainingSize > rangeSize) + m_totalRemainingSize = rangeSize; + } else + m_totalRemainingSize -= m_rangeOffset; +} + +int BlobResourceHandle::readSync(char* buf, int length) +{ + ASSERT(!m_async); + + int offset = 0; + int remaining = length; + while (remaining) { + // Do not continue if the request is aborted or an error occurs. + if (m_aborted || m_errorCode) + break; + + // If there is no more remaining data to read, we are done. + if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size()) + break; + + const BlobDataItem& item = m_blobData->items().at(m_readItemCount); + int bytesRead = 0; + if (item.type == BlobDataItem::Data) + bytesRead = readDataSync(item, buf + offset, remaining); + else if (item.type == BlobDataItem::File) + bytesRead = readFileSync(item, buf + offset, remaining); + else + ASSERT_NOT_REACHED(); + + if (bytesRead > 0) { + offset += bytesRead; + remaining -= bytesRead; + } + } + + int result; + if (m_aborted || m_errorCode) + result = -1; + else + result = length - remaining; + + notifyReceiveData(buf, result); + if (!result) + notifyFinish(); + + return result; +} + +int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int length) +{ + ASSERT(!m_async); + + long long remaining = item.length - m_currentItemReadSize; + 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); + m_totalRemainingSize -= bytesToRead; + + m_currentItemReadSize += bytesToRead; + if (m_currentItemReadSize == item.length) { + m_readItemCount++; + m_currentItemReadSize = 0; + } + + return bytesToRead; +} + +int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int length) +{ + ASSERT(!m_async); + + if (!m_fileOpened) { + long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize; + if (bytesToRead > m_totalRemainingSize) + bytesToRead = m_totalRemainingSize; + bool success = m_stream->openForRead(item.path, item.offset + m_currentItemReadSize, bytesToRead); + m_currentItemReadSize = 0; + if (!success) { + m_errorCode = notReadableError; + return 0; + } + + m_fileOpened = true; + } + + int bytesRead = m_stream->read(buf, length); + if (bytesRead < 0) { + m_errorCode = notReadableError; + return 0; + } + if (!bytesRead) { + m_stream->close(); + m_fileOpened = false; + m_readItemCount++; + } else + m_totalRemainingSize -= bytesRead; + + return bytesRead; +} + +void BlobResourceHandle::readAsync() +{ + ASSERT(m_async); + + // Do not continue if the request is aborted or an error occurs. + if (m_aborted || m_errorCode) + return; + + // If there is no more remaining data to read, we are done. + if (!m_totalRemainingSize || m_readItemCount >= m_blobData->items().size()) { + notifyFinish(); + return; + } + + const BlobDataItem& item = m_blobData->items().at(m_readItemCount); + if (item.type == BlobDataItem::Data) + readDataAsync(item); + else if (item.type == BlobDataItem::File) + readFileAsync(item); + else + ASSERT_NOT_REACHED(); +} + +void BlobResourceHandle::readDataAsync(const BlobDataItem& item) +{ + ASSERT(m_async); + + 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)); + m_currentItemReadSize = 0; +} + +void BlobResourceHandle::readFileAsync(const BlobDataItem& item) +{ + ASSERT(m_async); + + if (m_fileOpened) { + m_asyncStream->read(m_buffer.data(), m_buffer.size()); + return; + } + + long long bytesToRead = m_itemLengthList[m_readItemCount] - m_currentItemReadSize; + if (bytesToRead > m_totalRemainingSize) + bytesToRead = static_cast<int>(m_totalRemainingSize); + m_asyncStream->openForRead(item.path, item.offset + m_currentItemReadSize, bytesToRead); + m_fileOpened = true; + m_currentItemReadSize = 0; +} + +void BlobResourceHandle::didOpen(bool success) +{ + ASSERT(m_async); + + if (!success) { + failed(notReadableError); + return; + } + + // Continue the reading. + readAsync(); +} + +void BlobResourceHandle::didRead(int bytesRead) +{ + consumeData(m_buffer.data(), bytesRead); +} + +void BlobResourceHandle::consumeData(const char* data, int bytesRead) +{ + ASSERT(m_async); + + m_totalRemainingSize -= bytesRead; + + // Notify the client. + if (bytesRead) + notifyReceiveData(data, bytesRead); + + if (m_fileOpened) { + // When the current item is a file item, the reading is completed only if bytesRead is 0. + if (!bytesRead) { + // Close the file. + m_fileOpened = false; + m_asyncStream->close(); + + // Move to the next item. + m_readItemCount++; + } + } else { + // Otherwise, we read the current text item as a whole and move to the next item. + m_readItemCount++; + } + + // Continue the reading. + readAsync(); +} + +void BlobResourceHandle::failed(int errorCode) +{ + ASSERT(m_async); + + // Notify the client. + notifyFail(errorCode); + + // Close the file if needed. + if (m_fileOpened) { + m_fileOpened = false; + m_asyncStream->close(); + } +} + +void BlobResourceHandle::notifyResponse() +{ + if (!client()) + return; + + if (m_errorCode) { + notifyResponseOnError(); + notifyFinish(); + } else + notifyResponseOnSuccess(); +} + +void BlobResourceHandle::notifyResponseOnSuccess() +{ + bool isRangeRequest = m_rangeOffset != positionNotSpecified; + ResourceResponse response(firstRequest().url(), m_blobData->contentType(), m_totalRemainingSize, String(), String()); + response.setExpectedContentLength(m_totalRemainingSize); + response.setHTTPStatusCode(isRangeRequest ? httpPartialContent : httpOK); + response.setHTTPStatusText(isRangeRequest ? httpPartialContentText : httpOKText); + if (!m_blobData->contentDisposition().isEmpty()) + response.setHTTPHeaderField("Content-Disposition", m_blobData->contentDisposition()); + client()->didReceiveResponse(this, response); +} + +void BlobResourceHandle::notifyResponseOnError() +{ + ASSERT(m_errorCode); + + ResourceResponse response(firstRequest().url(), String(), 0, String(), String()); + switch (m_errorCode) { + case rangeError: + response.setHTTPStatusCode(httpRequestedRangeNotSatisfiable); + response.setHTTPStatusText(httpRequestedRangeNotSatisfiableText); + break; + case notFoundError: + response.setHTTPStatusCode(httpNotFound); + response.setHTTPStatusText(httpNotFoundText); + break; + case securityError: + response.setHTTPStatusCode(httpNotAllowed); + response.setHTTPStatusText(httpNotAllowedText); + break; + default: + response.setHTTPStatusCode(httpInternalError); + response.setHTTPStatusText(httpInternalErrorText); + break; + } + client()->didReceiveResponse(this, response); +} + +void BlobResourceHandle::notifyReceiveData(const char* data, int bytesRead) +{ + if (client()) + client()->didReceiveData(this, data, bytesRead, bytesRead); +} + +void BlobResourceHandle::notifyFail(int errorCode) +{ + if (client()) + client()->didFail(this, ResourceError(String(), errorCode, firstRequest().url(), String())); +} + +void BlobResourceHandle::notifyFinish() +{ + if (client()) + client()->didFinishLoading(this, 0); +} + +} // namespace WebCore + +#endif // ENABLE(BLOB) + diff --git a/Source/WebCore/platform/network/BlobResourceHandle.h b/Source/WebCore/platform/network/BlobResourceHandle.h new file mode 100644 index 0000000..63e8578 --- /dev/null +++ b/Source/WebCore/platform/network/BlobResourceHandle.h @@ -0,0 +1,116 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 BlobResourceHandle_h +#define BlobResourceHandle_h + +#if ENABLE(BLOB) + +#include "FileStreamClient.h" +#include "PlatformString.h" +#include "ResourceHandle.h" +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class AsyncFileStream; +class BlobStorageData; +class FileStream; +class ResourceHandleClient; +class ResourceRequest; +struct BlobDataItem; + +class BlobResourceHandle : public FileStreamClient, public ResourceHandle { +public: + static PassRefPtr<BlobResourceHandle> create(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async = true) + { + return adoptRef(new BlobResourceHandle(blobData, request, client, async)); + } + + static void loadResourceSynchronously(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data); + + // FileStreamClient methods. + virtual void didGetSize(long long); + virtual void didOpen(bool); + virtual void didRead(int); + + // ResourceHandle methods. + virtual void cancel(); + + void start(); + int readSync(char*, int); + +private: + BlobResourceHandle(PassRefPtr<BlobStorageData>, const ResourceRequest&, ResourceHandleClient*, bool async); + virtual ~BlobResourceHandle(); + + void getSizeForNext(); + void seek(); + void consumeData(const char* data, int bytesRead); + void failed(int errorCode); + + void readAsync(); + void readDataAsync(const BlobDataItem&); + void readFileAsync(const BlobDataItem&); + + int readDataSync(const BlobDataItem&, char*, int); + int readFileSync(const BlobDataItem&, char*, int); + + void notifyResponse(); + void notifyResponseOnSuccess(); + void notifyResponseOnError(); + void notifyReceiveData(const char*, int); + void notifyFail(int errorCode); + void notifyFinish(); + + RefPtr<BlobStorageData> m_blobData; + bool m_async; + RefPtr<AsyncFileStream> m_asyncStream; // For asynchronous loading. + RefPtr<FileStream> m_stream; // For synchronous loading. + Vector<char> m_buffer; + Vector<long long> m_itemLengthList; + int m_errorCode; + bool m_aborted; + long long m_rangeOffset; + long long m_rangeEnd; + long long m_rangeSuffixLength; + long long m_totalRemainingSize; + long long m_currentItemReadSize; + unsigned m_sizeItemCount; + unsigned m_readItemCount; + bool m_fileOpened; +}; + +} // namespace WebCore + +#endif // ENABLE(BLOB) + +#endif // BlobResourceHandle_h diff --git a/Source/WebCore/platform/network/BlobStorageData.h b/Source/WebCore/platform/network/BlobStorageData.h new file mode 100644 index 0000000..6535e62 --- /dev/null +++ b/Source/WebCore/platform/network/BlobStorageData.h @@ -0,0 +1,65 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 BlobStorageData_h +#define BlobStorageData_h + +#include "BlobData.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class BlobStorageData : public RefCounted<BlobStorageData> { +public: + static PassRefPtr<BlobStorageData> create(const String& contentType, const String& contentDisposition) + { + return adoptRef(new BlobStorageData(contentType, contentDisposition)); + } + + const String& contentType() const { return m_data.contentType(); } + const String& contentDisposition() const { return m_data.contentDisposition(); } + const BlobDataItemList& items() const { return m_data.items(); } + +private: + friend class BlobRegistryImpl; + + BlobStorageData(const String& contentType, const String& contentDisposition) + { + m_data.setContentType(contentType); + m_data.setContentDisposition(contentDisposition); + } + + BlobData m_data; +}; + +} // namespace WebCore + +#endif // BlobStorageData_h diff --git a/Source/WebCore/platform/network/CookieStorage.h b/Source/WebCore/platform/network/CookieStorage.h new file mode 100644 index 0000000..56ca5dc --- /dev/null +++ b/Source/WebCore/platform/network/CookieStorage.h @@ -0,0 +1,35 @@ +/* + * 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 CookieStorage_h +#define CookieStorage_h + +namespace WebCore { + +void setCookieStoragePrivateBrowsingEnabled(bool); + +} + +#endif diff --git a/Source/WebCore/platform/network/Credential.cpp b/Source/WebCore/platform/network/Credential.cpp new file mode 100644 index 0000000..4b24403 --- /dev/null +++ b/Source/WebCore/platform/network/Credential.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 "Credential.h" + +namespace WebCore { + +// Need to enforce empty, non-null strings due to the pickiness of the String == String operator +// combined with the semantics of the String(NSString*) constructor +Credential::Credential() + : m_user("") + , m_password("") + , m_persistence(CredentialPersistenceNone) +#if CERTIFICATE_CREDENTIALS_SUPPORTED + , m_type(CredentialTypePassword) +#endif +{ +} + +// Need to enforce empty, non-null strings due to the pickiness of the String == String operator +// combined with the semantics of the String(NSString*) constructor +Credential::Credential(const String& user, const String& password, CredentialPersistence persistence) + : m_user(user.length() ? user : "") + , m_password(password.length() ? password : "") + , m_persistence(persistence) +#if CERTIFICATE_CREDENTIALS_SUPPORTED + , m_type(CredentialTypePassword) +#endif +{ +} + +Credential::Credential(const Credential& original, CredentialPersistence persistence) + : m_user(original.user()) + , m_password(original.password()) + , m_persistence(persistence) +#if CERTIFICATE_CREDENTIALS_SUPPORTED + , m_identity(original.identity()) + , m_certificates(original.certificates()) + , m_type(original.type()) +#endif +{ +} + +bool Credential::isEmpty() const +{ +#if CERTIFICATE_CREDENTIALS_SUPPORTED + if (m_type == CredentialTypeClientCertificate && (m_identity || m_certificates)) + return false; +#endif + + return m_user.isEmpty() && m_password.isEmpty(); +} + +const String& Credential::user() const +{ + return m_user; +} + +const String& Credential::password() const +{ + return m_password; +} + +bool Credential::hasPassword() const +{ + return !m_password.isEmpty(); +} + +CredentialPersistence Credential::persistence() const +{ + return m_persistence; +} + +#if CERTIFICATE_CREDENTIALS_SUPPORTED +Credential::Credential(SecIdentityRef identity, CFArrayRef certificates, CredentialPersistence persistence) + : m_user("") + , m_password("") + , m_persistence(persistence) + , m_identity(identity) + , m_certificates(certificates) + , m_type(CredentialTypeClientCertificate) +{ +} + +SecIdentityRef Credential::identity() const +{ + return m_identity.get(); +} + +CFArrayRef Credential::certificates() const +{ + return m_certificates.get(); +} + +CredentialType Credential::type() const +{ + return m_type; +} +#endif + +bool operator==(const Credential& a, const Credential& b) +{ + // Check persistence first since all credential types + // have the persistence property. + if (a.persistence() != b.persistence()) + return false; + +#if CERTIFICATE_CREDENTIALS_SUPPORTED + CredentialType aType = a.type(); + if (aType != b.type()) + return false; + + // Comparing identity and certificate chain pointers is valid only + // for client certificate type credentials. + // + // FIXME: Is pointer comparison of the identity and certificates properties sufficient? + if (aType == CredentialTypeClientCertificate) { + if (a.identity() != b.identity()) + return false; + if (a.certificates() != b.certificates()) + return false; + + // We only need to check identity and certificates to compare + // client certificate based credentials. + return true; + } + + ASSERT(a.type() == CredentialTypePassword && b.type() == CredentialTypePassword); +#endif + + if (a.user() != b.user()) + return false; + if (a.password() != b.password()) + return false; + + return true; +} + +} + diff --git a/Source/WebCore/platform/network/Credential.h b/Source/WebCore/platform/network/Credential.h new file mode 100644 index 0000000..ee47887 --- /dev/null +++ b/Source/WebCore/platform/network/Credential.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 Credential_h +#define Credential_h + +#include "PlatformString.h" + +#define CERTIFICATE_CREDENTIALS_SUPPORTED ((PLATFORM(MAC) || PLATFORM(IOS)) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) + +#if CERTIFICATE_CREDENTIALS_SUPPORTED +#include <Security/SecBase.h> +#include <wtf/RetainPtr.h> +#endif + +namespace WebCore { + +enum CredentialPersistence { + CredentialPersistenceNone, + CredentialPersistenceForSession, + CredentialPersistencePermanent +}; + +#if CERTIFICATE_CREDENTIALS_SUPPORTED +enum CredentialType { + CredentialTypePassword, + CredentialTypeClientCertificate +}; +#endif + +class Credential { + +public: + Credential(); + Credential(const String& user, const String& password, CredentialPersistence); + Credential(const Credential& original, CredentialPersistence); +#if CERTIFICATE_CREDENTIALS_SUPPORTED + Credential(SecIdentityRef identity, CFArrayRef certificates, CredentialPersistence); +#endif + + bool isEmpty() const; + + const String& user() const; + const String& password() const; + bool hasPassword() const; + CredentialPersistence persistence() const; + +#if CERTIFICATE_CREDENTIALS_SUPPORTED + SecIdentityRef identity() const; + CFArrayRef certificates() const; + CredentialType type() const; +#endif + +private: + String m_user; + String m_password; + CredentialPersistence m_persistence; +#if CERTIFICATE_CREDENTIALS_SUPPORTED + RetainPtr<SecIdentityRef> m_identity; + RetainPtr<CFArrayRef> m_certificates; + CredentialType m_type; +#endif +}; + +bool operator==(const Credential& a, const Credential& b); +inline bool operator!=(const Credential& a, const Credential& b) { return !(a == b); } + +}; +#endif diff --git a/Source/WebCore/platform/network/CredentialStorage.cpp b/Source/WebCore/platform/network/CredentialStorage.cpp new file mode 100644 index 0000000..428181d --- /dev/null +++ b/Source/WebCore/platform/network/CredentialStorage.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2009 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. ``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. + */ + +#include "config.h" +#include "CredentialStorage.h" + +#include "Credential.h" +#include "KURL.h" +#include "ProtectionSpaceHash.h" +#include <wtf/text/StringConcatenate.h> +#include <wtf/text/StringHash.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +typedef HashMap<ProtectionSpace, Credential> ProtectionSpaceToCredentialMap; +static ProtectionSpaceToCredentialMap& protectionSpaceToCredentialMap() +{ + DEFINE_STATIC_LOCAL(ProtectionSpaceToCredentialMap, map, ()); + return map; +} + +static HashSet<String>& originsWithCredentials() +{ + DEFINE_STATIC_LOCAL(HashSet<String>, set, ()); + return set; +} + +typedef HashMap<String, ProtectionSpace> PathToDefaultProtectionSpaceMap; +static PathToDefaultProtectionSpaceMap& pathToDefaultProtectionSpaceMap() +{ + DEFINE_STATIC_LOCAL(PathToDefaultProtectionSpaceMap, map, ()); + return map; +} + +static String originStringFromURL(const KURL& url) +{ + if (url.port()) + return makeString(url.protocol(), "://", url.host(), ':', String::number(url.port()), '/'); + + return makeString(url.protocol(), "://", url.host(), '/'); +} + +static String protectionSpaceMapKeyFromURL(const KURL& url) +{ + ASSERT(url.isValid()); + + // Remove the last path component that is not a directory to determine the subtree for which credentials will apply. + // We keep a leading slash, but remove a trailing one. + String directoryURL = url.string().substring(0, url.pathEnd()); + unsigned directoryURLPathStart = url.pathStart(); + ASSERT(directoryURL[directoryURLPathStart] == '/'); + if (directoryURL.length() > directoryURLPathStart + 1) { + size_t index = directoryURL.reverseFind('/'); + ASSERT(index != notFound); + directoryURL = directoryURL.substring(0, (index != directoryURLPathStart) ? index : directoryURLPathStart + 1); + } + + return directoryURL; +} + +void CredentialStorage::set(const Credential& credential, const ProtectionSpace& protectionSpace, const KURL& url) +{ + ASSERT(protectionSpace.isProxy() || url.protocolInHTTPFamily()); + ASSERT(protectionSpace.isProxy() || url.isValid()); + + protectionSpaceToCredentialMap().set(protectionSpace, credential); + if (!protectionSpace.isProxy()) { + originsWithCredentials().add(originStringFromURL(url)); + + ProtectionSpaceAuthenticationScheme scheme = protectionSpace.authenticationScheme(); + if (scheme == ProtectionSpaceAuthenticationSchemeHTTPBasic || scheme == ProtectionSpaceAuthenticationSchemeDefault) { + // The map can contain both a path and its subpath - while redundant, this makes lookups faster. + pathToDefaultProtectionSpaceMap().set(protectionSpaceMapKeyFromURL(url), protectionSpace); + } + } +} + +Credential CredentialStorage::get(const ProtectionSpace& protectionSpace) +{ + return protectionSpaceToCredentialMap().get(protectionSpace); +} + +void CredentialStorage::remove(const ProtectionSpace& protectionSpace) +{ + protectionSpaceToCredentialMap().remove(protectionSpace); +} + +static PathToDefaultProtectionSpaceMap::iterator findDefaultProtectionSpaceForURL(const KURL& url) +{ + ASSERT(url.protocolInHTTPFamily()); + ASSERT(url.isValid()); + + PathToDefaultProtectionSpaceMap& map = pathToDefaultProtectionSpaceMap(); + + // Don't spend time iterating the path for origins that don't have any credentials. + if (!originsWithCredentials().contains(originStringFromURL(url))) + return map.end(); + + String directoryURL = protectionSpaceMapKeyFromURL(url); + unsigned directoryURLPathStart = url.pathStart(); + while (true) { + PathToDefaultProtectionSpaceMap::iterator iter = map.find(directoryURL); + if (iter != map.end()) + return iter; + + if (directoryURL.length() == directoryURLPathStart + 1) // path is "/" already, cannot shorten it any more + return map.end(); + + size_t index = directoryURL.reverseFind('/', directoryURL.length() - 2); + ASSERT(index != notFound); + directoryURL = directoryURL.substring(0, (index == directoryURLPathStart) ? index + 1 : index); + ASSERT(directoryURL.length() > directoryURLPathStart); + ASSERT(directoryURL.length() == directoryURLPathStart + 1 || directoryURL[directoryURL.length() - 1] != '/'); + } +} + +bool CredentialStorage::set(const Credential& credential, const KURL& url) +{ + ASSERT(url.protocolInHTTPFamily()); + ASSERT(url.isValid()); + PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url); + if (iter == pathToDefaultProtectionSpaceMap().end()) + return false; + ASSERT(originsWithCredentials().contains(originStringFromURL(url))); + protectionSpaceToCredentialMap().set(iter->second, credential); + return true; +} + +Credential CredentialStorage::get(const KURL& url) +{ + PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url); + if (iter == pathToDefaultProtectionSpaceMap().end()) + return Credential(); + return protectionSpaceToCredentialMap().get(iter->second); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/CredentialStorage.h b/Source/WebCore/platform/network/CredentialStorage.h new file mode 100644 index 0000000..d11384d --- /dev/null +++ b/Source/WebCore/platform/network/CredentialStorage.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2009 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. ``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 CredentialStorage_h +#define CredentialStorage_h + +namespace WebCore { + +class Credential; +class KURL; +class ProtectionSpace; + +class CredentialStorage { +public: + // WebCore session credential storage. + static void set(const Credential&, const ProtectionSpace&, const KURL&); + static Credential get(const ProtectionSpace&); + static void remove(const ProtectionSpace&); + + // OS persistent storage. + static Credential getFromPersistentStorage(const ProtectionSpace&); + + // These methods work for authentication schemes that support sending credentials without waiting for a request. E.g., for HTTP Basic authentication scheme + // a client should assume that all paths at or deeper than the depth of a known protected resource share are within the same protection space. + static bool set(const Credential&, const KURL&); // Returns true if the URL corresponds to a known protection space, so credentials could be updated. + static Credential get(const KURL&); +}; + +} // namespace WebCore + +#endif // CredentialStorage_h diff --git a/Source/WebCore/platform/network/DNS.h b/Source/WebCore/platform/network/DNS.h new file mode 100644 index 0000000..21d9c80 --- /dev/null +++ b/Source/WebCore/platform/network/DNS.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008 Collin Jackson <collinj@webkit.org> + * + * 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 DNS_h +#define DNS_h + +#include <wtf/Forward.h> + +namespace WebCore { + +#if !USE(SOUP) + void prefetchDNS(const String& hostname); +#endif +} + +#endif diff --git a/Source/WebCore/platform/network/DataURL.cpp b/Source/WebCore/platform/network/DataURL.cpp new file mode 100644 index 0000000..5de1b34 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/DataURL.h b/Source/WebCore/platform/network/DataURL.h new file mode 100644 index 0000000..33076a0 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/FormData.cpp b/Source/WebCore/platform/network/FormData.cpp new file mode 100644 index 0000000..16f98ad --- /dev/null +++ b/Source/WebCore/platform/network/FormData.cpp @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2004, 2006, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * 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 "FormData.h" + +#include "BlobData.h" +#include "BlobURL.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "Document.h" +#include "File.h" +#include "FileSystem.h" +#include "FormDataBuilder.h" +#include "FormDataList.h" +#include "MIMETypeRegistry.h" +#include "Page.h" +#include "TextEncoding.h" +#include <wtf/Decoder.h> +#include <wtf/Encoder.h> + +namespace WebCore { + +inline FormData::FormData() + : m_identifier(0) + , m_hasGeneratedFiles(false) + , m_alwaysStream(false) +{ +} + +inline FormData::FormData(const FormData& data) + : RefCounted<FormData>() + , m_elements(data.m_elements) + , m_identifier(data.m_identifier) + , m_hasGeneratedFiles(false) + , m_alwaysStream(false) +{ + // We shouldn't be copying FormData that hasn't already removed its generated files + // but just in case, make sure the new FormData is ready to generate its own files. + if (data.m_hasGeneratedFiles) { + size_t n = m_elements.size(); + for (size_t i = 0; i < n; ++i) { + FormDataElement& e = m_elements[i]; + if (e.m_type == FormDataElement::encodedFile) + e.m_generatedFilename = String(); + } + } +} + +FormData::~FormData() +{ + // This cleanup should've happened when the form submission finished. + // Just in case, let's assert, and do the cleanup anyway in release builds. + ASSERT(!m_hasGeneratedFiles); + removeGeneratedFilesIfNeeded(); +} + +PassRefPtr<FormData> FormData::create() +{ + return adoptRef(new FormData); +} + +PassRefPtr<FormData> FormData::create(const void* data, size_t size) +{ + RefPtr<FormData> result = create(); + result->appendData(data, size); + return result.release(); +} + +PassRefPtr<FormData> FormData::create(const CString& string) +{ + RefPtr<FormData> result = create(); + result->appendData(string.data(), string.length()); + return result.release(); +} + +PassRefPtr<FormData> FormData::create(const Vector<char>& vector) +{ + RefPtr<FormData> result = create(); + result->appendData(vector.data(), vector.size()); + return result.release(); +} + +PassRefPtr<FormData> FormData::create(const FormDataList& list, const TextEncoding& encoding) +{ + RefPtr<FormData> result = create(); + result->appendKeyValuePairItems(list, encoding, false, 0); + return result.release(); +} + +PassRefPtr<FormData> FormData::createMultiPart(const FormDataList& list, const TextEncoding& encoding, Document* document) +{ + RefPtr<FormData> result = create(); + result->appendKeyValuePairItems(list, encoding, true, document); + return result.release(); +} + +PassRefPtr<FormData> FormData::copy() const +{ + return adoptRef(new FormData(*this)); +} + +PassRefPtr<FormData> FormData::deepCopy() const +{ + RefPtr<FormData> formData(create()); + + formData->m_alwaysStream = m_alwaysStream; + + size_t n = m_elements.size(); + formData->m_elements.reserveInitialCapacity(n); + for (size_t i = 0; i < n; ++i) { + const FormDataElement& e = m_elements[i]; + switch (e.m_type) { + case FormDataElement::data: + formData->m_elements.append(FormDataElement(e.m_data)); + break; + case FormDataElement::encodedFile: +#if ENABLE(BLOB) + formData->m_elements.append(FormDataElement(e.m_filename, e.m_fileStart, e.m_fileLength, e.m_expectedFileModificationTime, e.m_shouldGenerateFile)); +#else + formData->m_elements.append(FormDataElement(e.m_filename, e.m_shouldGenerateFile)); +#endif + break; +#if ENABLE(BLOB) + case FormDataElement::encodedBlob: + formData->m_elements.append(FormDataElement(e.m_blobURL)); + break; +#endif + } + } + return formData.release(); +} + +void FormData::appendData(const void* data, size_t size) +{ + if (m_elements.isEmpty() || m_elements.last().m_type != FormDataElement::data) + m_elements.append(FormDataElement()); + FormDataElement& e = m_elements.last(); + size_t oldSize = e.m_data.size(); + e.m_data.grow(oldSize + size); + memcpy(e.m_data.data() + oldSize, data, size); +} + +void FormData::appendFile(const String& filename, bool shouldGenerateFile) +{ +#if ENABLE(BLOB) + m_elements.append(FormDataElement(filename, 0, BlobDataItem::toEndOfFile, BlobDataItem::doNotCheckFileChange, shouldGenerateFile)); +#else + m_elements.append(FormDataElement(filename, shouldGenerateFile)); +#endif +} + +#if ENABLE(BLOB) +void FormData::appendFileRange(const String& filename, long long start, long long length, double expectedModificationTime, bool shouldGenerateFile) +{ + m_elements.append(FormDataElement(filename, start, length, expectedModificationTime, shouldGenerateFile)); +} + +void FormData::appendBlob(const KURL& blobURL) +{ + m_elements.append(FormDataElement(blobURL)); +} +#endif + +void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncoding& encoding, bool isMultiPartForm, Document* document) +{ + if (isMultiPartForm) + m_boundary = FormDataBuilder::generateUniqueBoundaryString(); + + Vector<char> encodedData; + + const Vector<FormDataList::Item>& items = list.items(); + size_t formDataListSize = items.size(); + ASSERT(!(formDataListSize % 2)); + for (size_t i = 0; i < formDataListSize; i += 2) { + const FormDataList::Item& key = items[i]; + const FormDataList::Item& value = items[i + 1]; + if (isMultiPartForm) { + Vector<char> header; + FormDataBuilder::beginMultiPartHeader(header, m_boundary.data(), key.data()); + + bool shouldGenerateFile = false; + + // If the current type is blob, then we also need to include the filename + if (value.blob()) { + String name; + if (value.blob()->isFile()) { + // For file blob, use the filename (or relative path if it is present) as the name. + File* file = static_cast<File*>(value.blob()); +#if ENABLE(DIRECTORY_UPLOAD) + name = file->webkitRelativePath().isEmpty() ? file->name() : file->webkitRelativePath(); +#else + name = file->name(); +#endif + + // Let the application specify a filename if it's going to generate a replacement file for the upload. + if (Page* page = document->page()) { + String generatedFileName; + shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(file->path(), generatedFileName); + if (shouldGenerateFile) + name = generatedFileName; + } + } else { + // For non-file blob, use the identifier part of the URL as the name. + name = "Blob" + BlobURL::getIdentifier(value.blob()->url()); + name = name.replace("-", ""); // For safety, remove '-' from the filename since some servers may not like it. + } + + // We have to include the filename=".." part in the header, even if the filename is empty + FormDataBuilder::addFilenameToMultiPartHeader(header, encoding, name); + + // Add the content type if available, or "application/octet-stream" otherwise (RFC 1867). + String contentType; + if (value.blob()->type().isEmpty()) + contentType = "application/octet-stream"; + else + contentType = value.blob()->type(); + FormDataBuilder::addContentTypeToMultiPartHeader(header, contentType.latin1()); + } + + FormDataBuilder::finishMultiPartHeader(header); + + // Append body + appendData(header.data(), header.size()); + if (value.blob()) { + if (value.blob()->isFile()) { + // Do not add the file if the path is empty. + if (!static_cast<File*>(value.blob())->path().isEmpty()) + appendFile(static_cast<File*>(value.blob())->path(), shouldGenerateFile); + } +#if ENABLE(BLOB) + else + appendBlob(value.blob()->url()); +#endif + } else + appendData(value.data().data(), value.data().length()); + appendData("\r\n", 2); + } else { + // Omit the name "isindex" if it's the first form data element. + // FIXME: Why is this a good rule? Is this obsolete now? + if (encodedData.isEmpty() && key.data() == "isindex") + FormDataBuilder::encodeStringAsFormData(encodedData, value.data()); + else + FormDataBuilder::addKeyValuePairAsFormData(encodedData, key.data(), value.data()); + } + } + + if (isMultiPartForm) + FormDataBuilder::addBoundaryToMultiPartHeader(encodedData, m_boundary.data(), true); + + appendData(encodedData.data(), encodedData.size()); +} + +void FormData::flatten(Vector<char>& data) const +{ + // Concatenate all the byte arrays, but omit any files. + data.clear(); + size_t n = m_elements.size(); + for (size_t i = 0; i < n; ++i) { + const FormDataElement& e = m_elements[i]; + if (e.m_type == FormDataElement::data) + data.append(e.m_data.data(), static_cast<size_t>(e.m_data.size())); + } +} + +String FormData::flattenToString() const +{ + Vector<char> bytes; + flatten(bytes); + return Latin1Encoding().decode(reinterpret_cast<const char*>(bytes.data()), bytes.size()); +} + +void FormData::generateFiles(Document* document) +{ + ASSERT(!m_hasGeneratedFiles); + + if (m_hasGeneratedFiles) + return; + + Page* page = document->page(); + if (!page) + return; + ChromeClient* client = page->chrome()->client(); + + size_t n = m_elements.size(); + for (size_t i = 0; i < n; ++i) { + FormDataElement& e = m_elements[i]; + if (e.m_type == FormDataElement::encodedFile && e.m_shouldGenerateFile) { + e.m_generatedFilename = client->generateReplacementFile(e.m_filename); + m_hasGeneratedFiles = true; + } + } +} + +void FormData::removeGeneratedFilesIfNeeded() +{ + if (!m_hasGeneratedFiles) + return; + + size_t n = m_elements.size(); + for (size_t i = 0; i < n; ++i) { + FormDataElement& e = m_elements[i]; + if (e.m_type == FormDataElement::encodedFile && !e.m_generatedFilename.isEmpty()) { + ASSERT(e.m_shouldGenerateFile); + String directory = directoryName(e.m_generatedFilename); + deleteFile(e.m_generatedFilename); + deleteEmptyDirectory(directory); + e.m_generatedFilename = String(); + } + } + m_hasGeneratedFiles = false; +} + +static void encode(Encoder& encoder, const FormDataElement& element) +{ + encoder.encodeUInt32(element.m_type); + + switch (element.m_type) { + case FormDataElement::data: + encoder.encodeBytes(reinterpret_cast<const uint8_t*>(element.m_data.data()), element.m_data.size()); + return; + + case FormDataElement::encodedFile: + encoder.encodeString(element.m_filename); + encoder.encodeBool(element.m_shouldGenerateFile); +#if ENABLE(BLOB) + encoder.encodeInt64(element.m_fileStart); + encoder.encodeInt64(element.m_fileLength); + encoder.encodeDouble(element.m_expectedFileModificationTime); +#else + encoder.encodeInt64(0); + encoder.encodeInt64(0); + encoder.encodeDouble(0); +#endif + return; + +#if ENABLE(BLOB) + case FormDataElement::encodedBlob: + encoder.encodeString(element.m_blobURL.string()); + return; +#endif + } + + ASSERT_NOT_REACHED(); +} + +static bool decode(Decoder& decoder, FormDataElement& element) +{ + uint32_t type = element.m_type; + + switch (type) { + case FormDataElement::data: { + element.m_type = FormDataElement::data; + Vector<uint8_t> data; + if (!decoder.decodeBytes(data)) + return false; + size_t size = data.size(); + element.m_data.resize(size); + memcpy(element.m_data.data(), data.data(), size); + return true; + } + + case FormDataElement::encodedFile: { + element.m_type = FormDataElement::encodedFile; + if (!decoder.decodeString(element.m_filename)) + return false; + if (!decoder.decodeBool(element.m_shouldGenerateFile)) + return false; + int64_t fileStart; + if (!decoder.decodeInt64(fileStart)) + return false; + if (fileStart < 0) + return false; + int64_t fileLength; + if (!decoder.decodeInt64(fileLength)) + return false; + if (fileLength < fileStart) + return false; + double expectedFileModificationTime; + if (!decoder.decodeDouble(expectedFileModificationTime)) + return false; +#if ENABLE(BLOB) + element.m_fileStart = fileStart; + element.m_fileLength = fileLength; + element.m_expectedFileModificationTime = expectedFileModificationTime; +#endif + return true; + } + +#if ENABLE(BLOB) + case FormDataElement::encodedBlob: + element.m_type = FormDataElement::encodedBlob; + String blobURLString; + if (!decoder.decodeString(blobURLString)) + return false; + element.m_blobURL = KURL(KURL(), blobURLString); + return true; +#endif + } + + return false; +} + +void FormData::encodeForBackForward(Encoder& encoder) const +{ + encoder.encodeBool(m_alwaysStream); + + encoder.encodeBytes(reinterpret_cast<const uint8_t*>(m_boundary.data()), m_boundary.size()); + + size_t size = m_elements.size(); + encoder.encodeUInt64(size); + for (size_t i = 0; i < size; ++i) + encode(encoder, m_elements[i]); + + encoder.encodeBool(m_hasGeneratedFiles); + + encoder.encodeBool(m_identifier); +} + +PassRefPtr<FormData> FormData::decodeForBackForward(Decoder& decoder) +{ + RefPtr<FormData> data = FormData::create(); + + if (!decoder.decodeBool(data->m_alwaysStream)) + return 0; + + Vector<uint8_t> boundary; + if (!decoder.decodeBytes(boundary)) + return 0; + size_t size = boundary.size(); + data->m_boundary.resize(size); + memcpy(data->m_boundary.data(), boundary.data(), size); + + uint64_t elementsSize; + if (!decoder.decodeUInt64(elementsSize)) + return 0; + for (size_t i = 0; i < elementsSize; ++i) { + FormDataElement element; + if (!decode(decoder, element)) + return 0; + data->m_elements.append(element); + } + + if (!decoder.decodeBool(data->m_hasGeneratedFiles)) + return 0; + + if (!decoder.decodeInt64(data->m_identifier)) + return 0; + + return data.release(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/FormData.h b/Source/WebCore/platform/network/FormData.h new file mode 100644 index 0000000..85c932a --- /dev/null +++ b/Source/WebCore/platform/network/FormData.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2004, 2006, 2008, 2011 Apple Inc. All rights reserved. + * + * 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 FormData_h +#define FormData_h + +#include "KURL.h" +#include "PlatformString.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Document; +class FormDataList; +class TextEncoding; + +class FormDataElement { +public: + FormDataElement() : m_type(data) { } + explicit FormDataElement(const Vector<char>& array) : m_type(data), m_data(array) { } + +#if ENABLE(BLOB) + FormDataElement(const String& filename, long long fileStart, long long fileLength, double expectedFileModificationTime, bool shouldGenerateFile) : m_type(encodedFile), m_filename(filename), m_fileStart(fileStart), m_fileLength(fileLength), m_expectedFileModificationTime(expectedFileModificationTime), m_shouldGenerateFile(shouldGenerateFile) { } + explicit FormDataElement(const KURL& blobURL) : m_type(encodedBlob), m_blobURL(blobURL) { } +#else + FormDataElement(const String& filename, bool shouldGenerateFile) : m_type(encodedFile), m_filename(filename), m_shouldGenerateFile(shouldGenerateFile) { } +#endif + + enum { + data, + encodedFile +#if ENABLE(BLOB) + , encodedBlob +#endif + } m_type; + Vector<char> m_data; + String m_filename; +#if ENABLE(BLOB) + long long m_fileStart; + long long m_fileLength; + double m_expectedFileModificationTime; + KURL m_blobURL; +#endif + String m_generatedFilename; + bool m_shouldGenerateFile; +}; + +inline bool operator==(const FormDataElement& a, const FormDataElement& b) +{ + if (&a == &b) + return true; + + if (a.m_type != b.m_type) + return false; + if (a.m_type == FormDataElement::data) + return a.m_data == b.m_data; + if (a.m_type == FormDataElement::encodedFile) +#if ENABLE(BLOB) + return a.m_filename == b.m_filename && a.m_fileStart == b.m_fileStart && a.m_fileLength == b.m_fileLength && a.m_expectedFileModificationTime == b.m_expectedFileModificationTime; + if (a.m_type == FormDataElement::encodedBlob) + return a.m_blobURL == b.m_blobURL; +#else + return a.m_filename == b.m_filename; +#endif + + return true; +} + +inline bool operator!=(const FormDataElement& a, const FormDataElement& b) +{ + return !(a == b); +} + +class FormData : public RefCounted<FormData> { +public: + static PassRefPtr<FormData> create(); + static PassRefPtr<FormData> create(const void*, size_t); + static PassRefPtr<FormData> create(const CString&); + static PassRefPtr<FormData> create(const Vector<char>&); + static PassRefPtr<FormData> create(const FormDataList&, const TextEncoding&); + static PassRefPtr<FormData> createMultiPart(const FormDataList&, const TextEncoding&, Document*); + PassRefPtr<FormData> copy() const; + PassRefPtr<FormData> deepCopy() const; + ~FormData(); + + void encodeForBackForward(Encoder&) const; + static PassRefPtr<FormData> decodeForBackForward(Decoder&); + + void appendData(const void* data, size_t); + void appendFile(const String& filePath, bool shouldGenerateFile = false); +#if ENABLE(BLOB) + void appendFileRange(const String& filename, long long start, long long length, double expectedModificationTime, bool shouldGenerateFile = false); + void appendBlob(const KURL& blobURL); +#endif + + void flatten(Vector<char>&) const; // omits files + String flattenToString() const; // omits files + + bool isEmpty() const { return m_elements.isEmpty(); } + const Vector<FormDataElement>& elements() const { return m_elements; } + const Vector<char>& boundary() const { return m_boundary; } + + void generateFiles(Document*); + void removeGeneratedFilesIfNeeded(); + + bool alwaysStream() const { return m_alwaysStream; } + void setAlwaysStream(bool alwaysStream) { m_alwaysStream = alwaysStream; } + + // Identifies a particular form submission instance. A value of 0 is used + // to indicate an unspecified identifier. + void setIdentifier(int64_t identifier) { m_identifier = identifier; } + int64_t identifier() const { return m_identifier; } + +private: + FormData(); + FormData(const FormData&); + + void appendKeyValuePairItems(const FormDataList&, const TextEncoding&, bool isMultiPartForm, Document*); + + Vector<FormDataElement> m_elements; + + int64_t m_identifier; + bool m_hasGeneratedFiles; + bool m_alwaysStream; + Vector<char> m_boundary; +}; + +inline bool operator==(const FormData& a, const FormData& b) +{ + return a.elements() == b.elements(); +} + +inline bool operator!=(const FormData& a, const FormData& b) +{ + return !(a == b); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/FormDataBuilder.cpp b/Source/WebCore/platform/network/FormDataBuilder.cpp new file mode 100644 index 0000000..da28fc2 --- /dev/null +++ b/Source/WebCore/platform/network/FormDataBuilder.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * 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 "FormDataBuilder.h" + +#include "Document.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "TextEncoding.h" + +#include <limits> +#include <wtf/Assertions.h> +#include <wtf/text/CString.h> +#include <wtf/RandomNumber.h> + +namespace WebCore { + +// Helper functions +static inline void append(Vector<char>& buffer, char string) +{ + buffer.append(string); +} + +static inline void append(Vector<char>& buffer, const char* string) +{ + buffer.append(string, strlen(string)); +} + +static inline void append(Vector<char>& buffer, const CString& string) +{ + buffer.append(string.data(), string.length()); +} + +static void appendQuotedString(Vector<char>& buffer, const CString& string) +{ + // Append a string as a quoted value, escaping quotes and line breaks. + // FIXME: Is it correct to use percent escaping here? Other browsers do not encode these characters yet, + // so we should test popular servers to find out if there is an encoding form they can handle. + unsigned length = string.length(); + for (unsigned i = 0; i < length; ++i) { + unsigned char c = string.data()[i]; + + switch (c) { + case 0x0a: + append(buffer, "%0A"); + break; + case 0x0d: + append(buffer, "%0D"); + break; + case '"': + append(buffer, "%22"); + break; + default: + append(buffer, c); + } + } +} + +TextEncoding FormDataBuilder::encodingFromAcceptCharset(const String& acceptCharset, Document* document) +{ + String normalizedAcceptCharset = acceptCharset; + normalizedAcceptCharset.replace(',', ' '); + + Vector<String> charsets; + normalizedAcceptCharset.split(' ', charsets); + + TextEncoding encoding; + + Vector<String>::const_iterator end = charsets.end(); + for (Vector<String>::const_iterator it = charsets.begin(); it != end; ++it) { + if ((encoding = TextEncoding(*it)).isValid()) + return encoding; + } + + if (Frame* frame = document->frame()) + return frame->loader()->writer()->encoding(); + + return Latin1Encoding(); +} + +Vector<char> FormDataBuilder::generateUniqueBoundaryString() +{ + Vector<char> boundary; + + // The RFC 2046 spec says the alphanumeric characters plus the + // following characters are legal for boundaries: '()+_,-./:=? + // However the following characters, though legal, cause some sites + // to fail: (),./:=+ + // Note that our algorithm makes it twice as much likely for 'A' or 'B' + // to appear in the boundary string, because 0x41 and 0x42 are present in + // the below array twice. + static const char alphaNumericEncodingMap[64] = { + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, + 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42 + }; + + // Start with an informative prefix. + append(boundary, "----WebKitFormBoundary"); + + // Append 16 random 7bit ascii AlphaNumeric characters. + Vector<char> randomBytes; + + for (unsigned i = 0; i < 4; ++i) { + unsigned randomness = static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)); + randomBytes.append(alphaNumericEncodingMap[(randomness >> 24) & 0x3F]); + randomBytes.append(alphaNumericEncodingMap[(randomness >> 16) & 0x3F]); + randomBytes.append(alphaNumericEncodingMap[(randomness >> 8) & 0x3F]); + randomBytes.append(alphaNumericEncodingMap[randomness & 0x3F]); + } + + boundary.append(randomBytes); + boundary.append(0); // Add a 0 at the end so we can use this as a C-style string. + return boundary; +} + +void FormDataBuilder::beginMultiPartHeader(Vector<char>& buffer, const CString& boundary, const CString& name) +{ + addBoundaryToMultiPartHeader(buffer, boundary); + + // FIXME: This loses data irreversibly if the input name includes characters you can't encode + // in the website's character set. + append(buffer, "Content-Disposition: form-data; name=\""); + appendQuotedString(buffer, name); + append(buffer, '"'); +} + +void FormDataBuilder::addBoundaryToMultiPartHeader(Vector<char>& buffer, const CString& boundary, bool isLastBoundary) +{ + append(buffer, "--"); + append(buffer, boundary); + + if (isLastBoundary) + append(buffer, "--"); + + append(buffer, "\r\n"); +} + +void FormDataBuilder::addFilenameToMultiPartHeader(Vector<char>& buffer, const TextEncoding& encoding, const String& filename) +{ + // FIXME: This loses data irreversibly if the filename includes characters you can't encode + // in the website's character set. + append(buffer, "; filename=\""); + appendQuotedString(buffer, encoding.encode(filename.characters(), filename.length(), QuestionMarksForUnencodables)); + append(buffer, '"'); +} + +void FormDataBuilder::addContentTypeToMultiPartHeader(Vector<char>& buffer, const CString& mimeType) +{ + append(buffer, "\r\nContent-Type: "); + append(buffer, mimeType); +} + +void FormDataBuilder::finishMultiPartHeader(Vector<char>& buffer) +{ + append(buffer, "\r\n\r\n"); +} + +void FormDataBuilder::addKeyValuePairAsFormData(Vector<char>& buffer, const CString& key, const CString& value) +{ + if (!buffer.isEmpty()) + append(buffer, '&'); + + encodeStringAsFormData(buffer, key); + append(buffer, '='); + encodeStringAsFormData(buffer, value); +} + +void FormDataBuilder::encodeStringAsFormData(Vector<char>& buffer, const CString& string) +{ + static const char hexDigits[17] = "0123456789ABCDEF"; + + // Same safe characters as Netscape for compatibility. + static const char safeCharacters[] = "-._*"; + + // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 + unsigned length = string.length(); + for (unsigned i = 0; i < length; ++i) { + unsigned char c = string.data()[i]; + + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || strchr(safeCharacters, c)) + append(buffer, c); + else if (c == ' ') + append(buffer, '+'); + else if (c == '\n' || (c == '\r' && (i + 1 >= length || string.data()[i + 1] != '\n'))) + append(buffer, "%0D%0A"); + else if (c != '\r') { + append(buffer, '%'); + append(buffer, hexDigits[c >> 4]); + append(buffer, hexDigits[c & 0xF]); + } + } +} + +} diff --git a/Source/WebCore/platform/network/FormDataBuilder.h b/Source/WebCore/platform/network/FormDataBuilder.h new file mode 100644 index 0000000..87d0ef3 --- /dev/null +++ b/Source/WebCore/platform/network/FormDataBuilder.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * 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 FormDataBuilder_h +#define FormDataBuilder_h + +#include "PlatformString.h" +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class Document; +class TextEncoding; + +class FormDataBuilder : public Noncopyable { +public: + static TextEncoding encodingFromAcceptCharset(const String& acceptCharset, Document* document); + + // Helper functions used by HTMLFormElement/WMLGoElement for multi-part form data + static Vector<char> generateUniqueBoundaryString(); + static void beginMultiPartHeader(Vector<char>&, const CString& boundary, const CString& name); + static void addBoundaryToMultiPartHeader(Vector<char>&, const CString& boundary, bool isLastBoundary = false); + static void addFilenameToMultiPartHeader(Vector<char>&, const TextEncoding&, const String& filename); + static void addContentTypeToMultiPartHeader(Vector<char>&, const CString& mimeType); + static void finishMultiPartHeader(Vector<char>&); + + // Helper functions used by HTMLFormElement/WMLGoElement for non multi-part form data + static void addKeyValuePairAsFormData(Vector<char>&, const CString& key, const CString& value); + static void encodeStringAsFormData(Vector<char>&, const CString&); + +private: + FormDataBuilder() {} +}; + +} + +#endif diff --git a/Source/WebCore/platform/network/HTTPHeaderMap.cpp b/Source/WebCore/platform/network/HTTPHeaderMap.cpp new file mode 100644 index 0000000..92079a0 --- /dev/null +++ b/Source/WebCore/platform/network/HTTPHeaderMap.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "HTTPHeaderMap.h" + +#include <utility> + +using namespace std; + +namespace WebCore { + +HTTPHeaderMap::HTTPHeaderMap() +{ +} + +HTTPHeaderMap::~HTTPHeaderMap() +{ +} + +PassOwnPtr<CrossThreadHTTPHeaderMapData> HTTPHeaderMap::copyData() const +{ + OwnPtr<CrossThreadHTTPHeaderMapData> data(new CrossThreadHTTPHeaderMapData()); + data->reserveInitialCapacity(size()); + + HTTPHeaderMap::const_iterator end_it = end(); + for (HTTPHeaderMap::const_iterator it = begin(); it != end_it; ++it) { + data->append(make_pair(it->first.string().crossThreadString(), it->second.crossThreadString())); + } + return data.release(); +} + +void HTTPHeaderMap::adopt(PassOwnPtr<CrossThreadHTTPHeaderMapData> data) +{ + clear(); + size_t dataSize = data->size(); + for (size_t index = 0; index < dataSize; ++index) { + pair<String, String>& header = (*data)[index]; + 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) + { + return CaseFoldingHash::hash(cString, strlen(cString)); + } + + static bool equal(const AtomicString& key, const char* cString) + { + return equalIgnoringCase(key, cString); + } + + static void translate(AtomicString& location, const char* cString, unsigned /*hash*/) + { + location = AtomicString(cString); + } +}; + +String HTTPHeaderMap::get(const char* name) const +{ + const_iterator i = find<const char*, CaseFoldingCStringTranslator>(name); + if (i == end()) + return String(); + return i->second; +} + +bool HTTPHeaderMap::contains(const char* name) const +{ + return find<const char*, CaseFoldingCStringTranslator>(name) != end(); +} + +pair<HTTPHeaderMap::iterator, bool> HTTPHeaderMap::add(const char* name, const String& value) +{ + return HashMap<AtomicString, String, CaseFoldingHash>::add<const char*, CaseFoldingCStringTranslator>(name, value); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/HTTPHeaderMap.h b/Source/WebCore/platform/network/HTTPHeaderMap.h new file mode 100644 index 0000000..02071e3 --- /dev/null +++ b/Source/WebCore/platform/network/HTTPHeaderMap.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2009 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 COMPUTER, 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 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 HTTPHeaderMap_h +#define HTTPHeaderMap_h + +#include <utility> +#include <wtf/HashMap.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/AtomicStringHash.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + + typedef Vector<std::pair<String, String> > CrossThreadHTTPHeaderMapData; + + 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; + + 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; + String get(const char*) const; + pair<iterator, bool> add(const char* name, const String& value); + + }; + +} // namespace WebCore + +#endif // HTTPHeaderMap_h diff --git a/Source/WebCore/platform/network/HTTPParsers.cpp b/Source/WebCore/platform/network/HTTPParsers.cpp new file mode 100644 index 0000000..a1ba9d3 --- /dev/null +++ b/Source/WebCore/platform/network/HTTPParsers.cpp @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + * Copyright (C) 2009 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 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 "HTTPParsers.h" +#include "ResourceResponseBase.h" + +#include "PlatformString.h" +#include <wtf/text/CString.h> +#include <wtf/DateMath.h> + +using namespace WTF; + +namespace WebCore { + +// true if there is more to parse +static inline bool skipWhiteSpace(const String& str, unsigned& pos, bool fromHttpEquivMeta) +{ + unsigned len = str.length(); + + if (fromHttpEquivMeta) { + while (pos != len && str[pos] <= ' ') + ++pos; + } else { + while (pos != len && (str[pos] == '\t' || str[pos] == ' ')) + ++pos; + } + + return pos != len; +} + +// Returns true if the function can match the whole token (case insensitive). +// Note: Might return pos == str.length() +static inline bool skipToken(const String& str, unsigned& pos, const char* token) +{ + unsigned len = str.length(); + + while (pos != len && *token) { + if (toASCIILower(str[pos]) != *token++) + return false; + ++pos; + } + + return true; +} + +ContentDispositionType contentDispositionType(const String& contentDisposition) +{ + if (contentDisposition.isEmpty()) + return ContentDispositionNone; + + // Some broken sites just send + // Content-Disposition: ; filename="file" + // screen those out here. + if (contentDisposition.startsWith(";")) + return ContentDispositionNone; + + if (contentDisposition.startsWith("inline", false)) + return ContentDispositionInline; + + // Some broken sites just send + // Content-Disposition: filename="file" + // without a disposition token... screen those out. + if (contentDisposition.startsWith("filename", false)) + return ContentDispositionNone; + + // Also in use is Content-Disposition: name="file" + if (contentDisposition.startsWith("name", false)) + return ContentDispositionNone; + + // We have a content-disposition of "attachment" or unknown. + // RFC 2183, section 2.8 says that an unknown disposition + // value should be treated as "attachment" + return ContentDispositionAttachment; +} + +bool parseHTTPRefresh(const String& refresh, bool fromHttpEquivMeta, double& delay, String& url) +{ + unsigned len = refresh.length(); + unsigned pos = 0; + + if (!skipWhiteSpace(refresh, pos, fromHttpEquivMeta)) + return false; + + while (pos != len && refresh[pos] != ',' && refresh[pos] != ';') + ++pos; + + if (pos == len) { // no URL + url = String(); + bool ok; + delay = refresh.stripWhiteSpace().toDouble(&ok); + return ok; + } else { + bool ok; + delay = refresh.left(pos).stripWhiteSpace().toDouble(&ok); + if (!ok) + return false; + + ++pos; + skipWhiteSpace(refresh, pos, fromHttpEquivMeta); + unsigned urlStartPos = pos; + if (refresh.find("url", urlStartPos, false) == urlStartPos) { + urlStartPos += 3; + skipWhiteSpace(refresh, urlStartPos, fromHttpEquivMeta); + if (refresh[urlStartPos] == '=') { + ++urlStartPos; + skipWhiteSpace(refresh, urlStartPos, fromHttpEquivMeta); + } else + urlStartPos = pos; // e.g. "Refresh: 0; url.html" + } + + unsigned urlEndPos = len; + + if (refresh[urlStartPos] == '"' || refresh[urlStartPos] == '\'') { + UChar quotationMark = refresh[urlStartPos]; + urlStartPos++; + while (urlEndPos > urlStartPos) { + urlEndPos--; + if (refresh[urlEndPos] == quotationMark) + break; + } + + // https://bugs.webkit.org/show_bug.cgi?id=27868 + // Sometimes there is no closing quote for the end of the URL even though there was an opening quote. + // If we looped over the entire alleged URL string back to the opening quote, just go ahead and use everything + // after the opening quote instead. + if (urlEndPos == urlStartPos) + urlEndPos = len; + } + + url = refresh.substring(urlStartPos, urlEndPos - urlStartPos).stripWhiteSpace(); + return true; + } +} + +double parseDate(const String& value) +{ + return parseDateFromNullTerminatedCharacters(value.utf8().data()); +} + +String filenameFromHTTPContentDisposition(const String& value) +{ + Vector<String> keyValuePairs; + value.split(';', keyValuePairs); + + unsigned length = keyValuePairs.size(); + for (unsigned i = 0; i < length; i++) { + size_t valueStartPos = keyValuePairs[i].find('='); + if (valueStartPos == notFound) + continue; + + String key = keyValuePairs[i].left(valueStartPos).stripWhiteSpace(); + + if (key.isEmpty() || key != "filename") + continue; + + String value = keyValuePairs[i].substring(valueStartPos + 1).stripWhiteSpace(); + + // Remove quotes if there are any + if (value[0] == '\"') + value = value.substring(1, value.length() - 2); + + return value; + } + + return String(); +} + +String extractMIMETypeFromMediaType(const String& mediaType) +{ + Vector<UChar, 64> mimeType; + unsigned length = mediaType.length(); + mimeType.reserveCapacity(length); + for (unsigned i = 0; i < length; i++) { + UChar c = mediaType[i]; + + if (c == ';') + break; + + // While RFC 2616 does not allow it, other browsers allow multiple values in the HTTP media + // type header field, Content-Type. In such cases, the media type string passed here may contain + // the multiple values separated by commas. For now, this code ignores text after the first comma, + // which prevents it from simply failing to parse such types altogether. Later for better + // compatibility we could consider using the first or last valid MIME type instead. + // See https://bugs.webkit.org/show_bug.cgi?id=25352 for more discussion. + if (c == ',') + break; + + // FIXME: The following is not correct. RFC 2616 allows linear white space before and + // after the MIME type, but not within the MIME type itself. And linear white space + // includes only a few specific ASCII characters; a small subset of isSpaceOrNewline. + // See https://bugs.webkit.org/show_bug.cgi?id=8644 for a bug tracking part of this. + if (isSpaceOrNewline(c)) + continue; + + mimeType.append(c); + } + + if (mimeType.size() == length) + return mediaType; + return String(mimeType.data(), mimeType.size()); +} + +String extractCharsetFromMediaType(const String& mediaType) +{ + unsigned int pos, len; + findCharsetInMediaType(mediaType, pos, len); + return mediaType.substring(pos, len); +} + +void findCharsetInMediaType(const String& mediaType, unsigned int& charsetPos, unsigned int& charsetLen, unsigned int start) +{ + charsetPos = start; + charsetLen = 0; + + size_t pos = start; + unsigned length = mediaType.length(); + + while (pos < length) { + pos = mediaType.find("charset", pos, false); + if (pos == notFound || pos == 0) { + charsetLen = 0; + return; + } + + // is what we found a beginning of a word? + if (mediaType[pos-1] > ' ' && mediaType[pos-1] != ';') { + pos += 7; + continue; + } + + pos += 7; + + // skip whitespace + while (pos != length && mediaType[pos] <= ' ') + ++pos; + + if (mediaType[pos++] != '=') // this "charset" substring wasn't a parameter name, but there may be others + continue; + + while (pos != length && (mediaType[pos] <= ' ' || mediaType[pos] == '"' || mediaType[pos] == '\'')) + ++pos; + + // we don't handle spaces within quoted parameter values, because charset names cannot have any + unsigned endpos = pos; + while (pos != length && mediaType[endpos] > ' ' && mediaType[endpos] != '"' && mediaType[endpos] != '\'' && mediaType[endpos] != ';') + ++endpos; + + charsetPos = pos; + charsetLen = endpos - pos; + return; + } +} + +XSSProtectionDisposition parseXSSProtectionHeader(const String& header) +{ + String stippedHeader = header.stripWhiteSpace(); + + if (stippedHeader.isEmpty()) + return XSSProtectionEnabled; + + if (stippedHeader[0] == '0') + return XSSProtectionDisabled; + + unsigned length = header.length(); + unsigned pos = 0; + if (stippedHeader[pos++] == '1' + && skipWhiteSpace(stippedHeader, pos, false) + && stippedHeader[pos++] == ';' + && skipWhiteSpace(stippedHeader, pos, false) + && skipToken(stippedHeader, pos, "mode") + && skipWhiteSpace(stippedHeader, pos, false) + && stippedHeader[pos++] == '=' + && skipWhiteSpace(stippedHeader, pos, false) + && skipToken(stippedHeader, pos, "block") + && pos == length) + return XSSProtectionBlockEnabled; + + return XSSProtectionEnabled; +} + +String extractReasonPhraseFromHTTPStatusLine(const String& statusLine) +{ + size_t spacePos = statusLine.find(' '); + // Remove status code from the status line. + spacePos = statusLine.find(' ', spacePos + 1); + return statusLine.substring(spacePos + 1); +} + +bool parseRange(const String& range, long long& rangeOffset, long long& rangeEnd, long long& rangeSuffixLength) +{ + // The format of "Range" header is defined in RFC 2616 Section 14.35.1. + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1 + // We don't support multiple range requests. + + rangeOffset = rangeEnd = rangeSuffixLength = -1; + + // The "bytes" unit identifier should be present. + static const char bytesStart[] = "bytes="; + if (!range.startsWith(bytesStart, false)) + return false; + String byteRange = range.substring(sizeof(bytesStart) - 1); + + // The '-' character needs to be present. + int index = byteRange.find('-'); + if (index == -1) + return false; + + // If the '-' character is at the beginning, the suffix length, which specifies the last N bytes, is provided. + // Example: + // -500 + if (!index) { + String suffixLengthString = byteRange.substring(index + 1).stripWhiteSpace(); + bool ok; + long long value = suffixLengthString.toInt64Strict(&ok); + if (ok) + rangeSuffixLength = value; + return true; + } + + // Otherwise, the first-byte-position and the last-byte-position are provied. + // Examples: + // 0-499 + // 500- + String firstBytePosStr = byteRange.left(index).stripWhiteSpace(); + bool ok; + long long firstBytePos = firstBytePosStr.toInt64Strict(&ok); + if (!ok) + return false; + + String lastBytePosStr = byteRange.substring(index + 1).stripWhiteSpace(); + long long lastBytePos = -1; + if (!lastBytePosStr.isEmpty()) { + lastBytePos = lastBytePosStr.toInt64Strict(&ok); + if (!ok) + return false; + } + + if (firstBytePos < 0 || !(lastBytePos == -1 || lastBytePos >= firstBytePos)) + return false; + + rangeOffset = firstBytePos; + rangeEnd = lastBytePos; + return true; +} + +} diff --git a/Source/WebCore/platform/network/HTTPParsers.h b/Source/WebCore/platform/network/HTTPParsers.h new file mode 100644 index 0000000..55b8c7b --- /dev/null +++ b/Source/WebCore/platform/network/HTTPParsers.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2009 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 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 HTTPParsers_h +#define HTTPParsers_h + +#include <wtf/Forward.h> + +namespace WebCore { + +class ResourceResponseBase; + +enum XSSProtectionDisposition { + XSSProtectionDisabled, + XSSProtectionEnabled, + XSSProtectionBlockEnabled +}; + +typedef enum { + ContentDispositionNone, + ContentDispositionInline, + ContentDispositionAttachment, + ContentDispositionOther +} ContentDispositionType; + +ContentDispositionType contentDispositionType(const String&); +bool parseHTTPRefresh(const String& refresh, bool fromHttpEquivMeta, double& delay, String& url); +double parseDate(const String&); +String filenameFromHTTPContentDisposition(const String&); +String extractMIMETypeFromMediaType(const String&); +String extractCharsetFromMediaType(const String&); +void findCharsetInMediaType(const String& mediaType, unsigned int& charsetPos, unsigned int& charsetLen, unsigned int start = 0); +XSSProtectionDisposition parseXSSProtectionHeader(const String&); +String extractReasonPhraseFromHTTPStatusLine(const String&); + +// -1 could be set to one of the return parameters to indicate the value is not specified. +bool parseRange(const String&, long long& rangeOffset, long long& rangeEnd, long long& rangeSuffixLength); + +} + +#endif diff --git a/Source/WebCore/platform/network/NetworkStateNotifier.cpp b/Source/WebCore/platform/network/NetworkStateNotifier.cpp new file mode 100644 index 0000000..7638478 --- /dev/null +++ b/Source/WebCore/platform/network/NetworkStateNotifier.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 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. ``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. + */ + +#include "config.h" +#include "NetworkStateNotifier.h" + +#include <wtf/Assertions.h> +#include <wtf/StdLibExtras.h> +#include <wtf/Threading.h> + +namespace WebCore { + +NetworkStateNotifier& networkStateNotifier() +{ + AtomicallyInitializedStatic(NetworkStateNotifier*, networkStateNotifier = new NetworkStateNotifier); + + return *networkStateNotifier; +} + +void NetworkStateNotifier::setNetworkStateChangedFunction(void(*function)()) +{ + ASSERT(!m_networkStateChangedFunction); + + m_networkStateChangedFunction = function; +} + +} diff --git a/Source/WebCore/platform/network/NetworkStateNotifier.h b/Source/WebCore/platform/network/NetworkStateNotifier.h new file mode 100644 index 0000000..21d0067 --- /dev/null +++ b/Source/WebCore/platform/network/NetworkStateNotifier.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2008 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. ``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 NetworkStateNotifier_h +#define NetworkStateNotifier_h + +#include <wtf/Noncopyable.h> +#if PLATFORM(ANDROID) +// TODO: Upstream to webkit.org +#include "Connection.h" +#endif + +#if PLATFORM(MAC) + +#include <wtf/RetainPtr.h> +#include "Timer.h" + +typedef const struct __CFArray * CFArrayRef; +typedef const struct __SCDynamicStore * SCDynamicStoreRef; + +#elif PLATFORM(CHROMIUM) + +#include "NetworkStateNotifierPrivate.h" + +#elif PLATFORM(WIN) + +#include <windows.h> + +#elif PLATFORM(QT) + +#include <QtCore/qglobal.h> + +#ifdef QT_NO_BEARERMANAGEMENT +#undef ENABLE_QT_BEARER +#define ENABLE_QT_BEARER 0 +#endif + +#endif + +namespace WebCore { + +#if (PLATFORM(QT) && ENABLE(QT_BEARER)) +class NetworkStateNotifierPrivate; +#endif + +class NetworkStateNotifier : public Noncopyable { +public: + NetworkStateNotifier(); + void setNetworkStateChangedFunction(void (*)()); + + bool onLine() const { return m_isOnLine; } + +#if (PLATFORM(QT) && ENABLE(QT_BEARER)) + void setNetworkAccessAllowed(bool); +#endif + +#if PLATFORM(ANDROID) + // TODO: Upstream to webkit.org + Connection::ConnectionType type() const { return m_type; } +#endif + +private: + bool m_isOnLine; +#if PLATFORM(ANDROID) + // TODO: Upstream to webkit.org + Connection::ConnectionType m_type; +#endif + void (*m_networkStateChangedFunction)(); + + void updateState(); + +#if PLATFORM(MAC) + void networkStateChangeTimerFired(Timer<NetworkStateNotifier>*); + + static void dynamicStoreCallback(SCDynamicStoreRef, CFArrayRef changedKeys, void *info); + + RetainPtr<SCDynamicStoreRef> m_store; + Timer<NetworkStateNotifier> m_networkStateChangeTimer; + +#elif PLATFORM(WIN) + static void CALLBACK addrChangeCallback(void*, BOOLEAN timedOut); + static void callAddressChanged(void*); + void addressChanged(); + + void registerForAddressChange(); + HANDLE m_waitHandle; + OVERLAPPED m_overlapped; + +#elif PLATFORM(CHROMIUM) + NetworkStateNotifierPrivate p; + +#elif PLATFORM(ANDROID) +public: + void networkStateChange(bool online); + // TODO: Upstream to webkit.org + void networkTypeChange(Connection::ConnectionType type); + +#elif PLATFORM(QT) && ENABLE(QT_BEARER) + friend class NetworkStateNotifierPrivate; + NetworkStateNotifierPrivate* p; +#endif +}; + +#if !PLATFORM(MAC) && !PLATFORM(WIN) && !PLATFORM(CHROMIUM) && !(PLATFORM(QT) && ENABLE(QT_BEARER)) + +inline NetworkStateNotifier::NetworkStateNotifier() + : m_isOnLine(true) +#if PLATFORM(ANDROID) + // TODO: Upstream to webkit.org + , m_type(Connection::UNKNOWN) +#endif + , m_networkStateChangedFunction(0) +{ +} + +inline void NetworkStateNotifier::updateState() { } + +#endif + +NetworkStateNotifier& networkStateNotifier(); + +}; + +#endif // NetworkStateNotifier_h diff --git a/Source/WebCore/platform/network/NetworkingContext.h b/Source/WebCore/platform/network/NetworkingContext.h new file mode 100644 index 0000000..e8a3776 --- /dev/null +++ b/Source/WebCore/platform/network/NetworkingContext.h @@ -0,0 +1,80 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + 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 NetworkingContext_h +#define NetworkingContext_h + +#include <wtf/RefCounted.h> + +#if PLATFORM(MAC) +#include "SchedulePair.h" +#endif + +#if PLATFORM(QT) +QT_BEGIN_NAMESPACE +class QObject; +class QNetworkAccessManager; +QT_END_NAMESPACE +#endif + +namespace WebCore { + +#if PLATFORM(ANDROID) +class FrameLoaderClient; +class MainResourceLoader; +#endif +class ResourceError; +class ResourceRequest; + +class NetworkingContext : public RefCounted<NetworkingContext> { +public: + virtual ~NetworkingContext() { } + + virtual bool isValid() const { return true; } + +#if PLATFORM(MAC) + virtual bool needsSiteSpecificQuirks() const = 0; + virtual bool localFileContentSniffingEnabled() const = 0; + virtual SchedulePairHashSet* scheduledRunLoopPairs() const = 0; + virtual ResourceError blockedError(const ResourceRequest&) const = 0; +#endif + +#if PLATFORM(QT) + virtual QObject* originatingObject() const = 0; + virtual QNetworkAccessManager* networkAccessManager() const = 0; +#endif + +#if PLATFORM(WIN) + virtual String userAgent() const = 0; + virtual String referrer() const = 0; + virtual ResourceError blockedError(const ResourceRequest&) const = 0; +#endif + +#if PLATFORM(ANDROID) + virtual MainResourceLoader* mainResourceLoader() const = 0; + virtual FrameLoaderClient* frameLoaderClient() const = 0; +#endif + +protected: + NetworkingContext() { } +}; + +} + +#endif // NetworkingContext_h diff --git a/Source/WebCore/platform/network/ProtectionSpace.cpp b/Source/WebCore/platform/network/ProtectionSpace.cpp new file mode 100644 index 0000000..26c258f --- /dev/null +++ b/Source/WebCore/platform/network/ProtectionSpace.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 "ProtectionSpace.h" + +#if USE(CFNETWORK) && !PLATFORM(MAC) +#include "AuthenticationCF.h" +#include <CFNetwork/CFURLProtectionSpacePriv.h> +#include <wtf/RetainPtr.h> +#endif + +namespace WebCore { + +// Need to enforce empty, non-null strings due to the pickiness of the String == String operator +// combined with the semantics of the String(NSString*) constructor +ProtectionSpace::ProtectionSpace() + : m_host("") + , m_port(0) + , m_serverType(ProtectionSpaceServerHTTP) + , m_realm("") + , m_authenticationScheme(ProtectionSpaceAuthenticationSchemeDefault) + , m_isHashTableDeletedValue(false) +{ +} + +// Need to enforce empty, non-null strings due to the pickiness of the String == String operator +// combined with the semantics of the String(NSString*) constructor +ProtectionSpace::ProtectionSpace(const String& host, int port, ProtectionSpaceServerType serverType, const String& realm, ProtectionSpaceAuthenticationScheme authenticationScheme) + : m_host(host.length() ? host : "") + , m_port(port) + , m_serverType(serverType) + , m_realm(realm.length() ? realm : "") + , m_authenticationScheme(authenticationScheme) + , m_isHashTableDeletedValue(false) +{ +} + +const String& ProtectionSpace::host() const +{ + return m_host; +} + +int ProtectionSpace::port() const +{ + return m_port; +} + +ProtectionSpaceServerType ProtectionSpace::serverType() const +{ + return m_serverType; +} + +bool ProtectionSpace::isProxy() const +{ + return (m_serverType == ProtectionSpaceProxyHTTP || + m_serverType == ProtectionSpaceProxyHTTPS || + m_serverType == ProtectionSpaceProxyFTP || + m_serverType == ProtectionSpaceProxySOCKS); +} + +const String& ProtectionSpace::realm() const +{ + return m_realm; +} + +ProtectionSpaceAuthenticationScheme ProtectionSpace::authenticationScheme() const +{ + return m_authenticationScheme; +} + +bool ProtectionSpace::receivesCredentialSecurely() const +{ +#if USE(CFNETWORK) && !PLATFORM(MAC) + RetainPtr<CFURLProtectionSpaceRef> cfSpace(AdoptCF, createCF(*this)); + return cfSpace && CFURLProtectionSpaceReceivesCredentialSecurely(cfSpace.get()); +#else + return (m_serverType == ProtectionSpaceServerHTTPS || + m_serverType == ProtectionSpaceServerFTPS || + m_serverType == ProtectionSpaceProxyHTTPS || + m_authenticationScheme == ProtectionSpaceAuthenticationSchemeHTTPDigest); +#endif +} + +bool operator==(const ProtectionSpace& a, const ProtectionSpace& b) +{ + if (a.host() != b.host()) + return false; + if (a.port() != b.port()) + return false; + if (a.serverType() != b.serverType()) + return false; + // Ignore realm for proxies + if (!a.isProxy() && a.realm() != b.realm()) + return false; + if (a.authenticationScheme() != b.authenticationScheme()) + return false; + + return true; +} + +} + + diff --git a/Source/WebCore/platform/network/ProtectionSpace.h b/Source/WebCore/platform/network/ProtectionSpace.h new file mode 100644 index 0000000..deb59d2 --- /dev/null +++ b/Source/WebCore/platform/network/ProtectionSpace.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 ProtectionSpace_h +#define ProtectionSpace_h + +#include "PlatformString.h" + +namespace WebCore { + +enum ProtectionSpaceServerType { + ProtectionSpaceServerHTTP = 1, + ProtectionSpaceServerHTTPS = 2, + ProtectionSpaceServerFTP = 3, + ProtectionSpaceServerFTPS = 4, + ProtectionSpaceProxyHTTP = 5, + ProtectionSpaceProxyHTTPS = 6, + ProtectionSpaceProxyFTP = 7, + ProtectionSpaceProxySOCKS = 8 +}; + +enum ProtectionSpaceAuthenticationScheme { + ProtectionSpaceAuthenticationSchemeDefault = 1, + ProtectionSpaceAuthenticationSchemeHTTPBasic = 2, + ProtectionSpaceAuthenticationSchemeHTTPDigest = 3, + ProtectionSpaceAuthenticationSchemeHTMLForm = 4, + ProtectionSpaceAuthenticationSchemeNTLM = 5, + ProtectionSpaceAuthenticationSchemeNegotiate = 6, + ProtectionSpaceAuthenticationSchemeClientCertificateRequested = 7, + ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested = 8, + ProtectionSpaceAuthenticationSchemeUnknown = 100, +}; + +class ProtectionSpace { + +public: + ProtectionSpace(); + ProtectionSpace(const String& host, int port, ProtectionSpaceServerType, const String& realm, ProtectionSpaceAuthenticationScheme); + + // Hash table deleted values, which are only constructed and never copied or destroyed. + ProtectionSpace(WTF::HashTableDeletedValueType) : m_isHashTableDeletedValue(true) { } + bool isHashTableDeletedValue() const { return m_isHashTableDeletedValue; } + + const String& host() const; + int port() const; + ProtectionSpaceServerType serverType() const; + bool isProxy() const; + const String& realm() const; + ProtectionSpaceAuthenticationScheme authenticationScheme() const; + + bool receivesCredentialSecurely() const; + +private: + String m_host; + int m_port; + ProtectionSpaceServerType m_serverType; + String m_realm; + ProtectionSpaceAuthenticationScheme m_authenticationScheme; + bool m_isHashTableDeletedValue; +}; + +bool operator==(const ProtectionSpace& a, const ProtectionSpace& b); +inline bool operator!=(const ProtectionSpace& a, const ProtectionSpace& b) { return !(a == b); } + +} // namespace WebCore + +#endif // ProtectionSpace_h diff --git a/Source/WebCore/platform/network/ProtectionSpaceHash.h b/Source/WebCore/platform/network/ProtectionSpaceHash.h new file mode 100644 index 0000000..08716b5 --- /dev/null +++ b/Source/WebCore/platform/network/ProtectionSpaceHash.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009 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. ``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 ProtectionSpaceHash_h +#define ProtectionSpaceHash_h + +#include "ProtectionSpace.h" +#include <wtf/HashTraits.h> + +namespace WebCore { + +struct ProtectionSpaceHash { + static unsigned hash(const ProtectionSpace& protectionSpace) + { + unsigned hashCodes[5] = { + protectionSpace.host().impl() ? protectionSpace.host().impl()->hash() : 0, + protectionSpace.port(), + protectionSpace.serverType(), + protectionSpace.authenticationScheme(), + protectionSpace.realm().impl() ? protectionSpace.realm().impl()->hash() : 0 + }; + + unsigned codeCount = sizeof(hashCodes) / sizeof(UChar); + // Ignore realm for proxies. + if (protectionSpace.isProxy()) + codeCount -= sizeof(hashCodes[0]) / sizeof(UChar); + return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), codeCount); + } + + static bool equal(const ProtectionSpace& a, const ProtectionSpace& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +} // namespace WebCore + +namespace WTF { + + // WebCore::ProtectionSpaceHash is the default hash for ProtectionSpace + template<> struct HashTraits<WebCore::ProtectionSpace> : GenericHashTraits<WebCore::ProtectionSpace> { + static const bool emptyValueIsZero = true; + static void constructDeletedValue(WebCore::ProtectionSpace& slot) { new (&slot) WebCore::ProtectionSpace(HashTableDeletedValue); } + static bool isDeletedValue(const WebCore::ProtectionSpace& slot) { return slot.isHashTableDeletedValue(); } + }; + + template<typename T> struct DefaultHash; + template<> struct DefaultHash<WebCore::ProtectionSpace> { + typedef WebCore::ProtectionSpaceHash Hash; + }; + +} // namespace WTF + + +#endif // ProtectionSpaceHash_h diff --git a/Source/WebCore/platform/network/ProxyServer.cpp b/Source/WebCore/platform/network/ProxyServer.cpp new file mode 100644 index 0000000..7ef283b --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/ProxyServer.h b/Source/WebCore/platform/network/ProxyServer.h new file mode 100644 index 0000000..8f7612e --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/ResourceErrorBase.cpp b/Source/WebCore/platform/network/ResourceErrorBase.cpp new file mode 100644 index 0000000..42bc0de --- /dev/null +++ b/Source/WebCore/platform/network/ResourceErrorBase.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2009 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 COMPUTER, 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 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 "ResourceError.h" + +namespace WebCore { + +const char* const errorDomainWebKitInternal = "WebKitInternal"; + +ResourceError ResourceErrorBase::copy() const +{ + lazyInit(); + + ResourceError errorCopy; + errorCopy.m_domain = m_domain.crossThreadString(); + errorCopy.m_errorCode = m_errorCode; + errorCopy.m_failingURL = m_failingURL.crossThreadString(); + errorCopy.m_localizedDescription = m_localizedDescription.crossThreadString(); + errorCopy.m_isNull = m_isNull; + errorCopy.m_isCancellation = m_isCancellation; + return errorCopy; +} + +void ResourceErrorBase::lazyInit() const +{ + const_cast<ResourceError*>(static_cast<const ResourceError*>(this))->platformLazyInit(); +} + +bool ResourceErrorBase::compare(const ResourceError& a, const ResourceError& b) +{ + if (a.isNull() && b.isNull()) + return true; + + if (a.isNull() || b.isNull()) + return false; + + if (a.domain() != b.domain()) + return false; + + if (a.errorCode() != b.errorCode()) + return false; + + if (a.failingURL() != b.failingURL()) + return false; + + if (a.localizedDescription() != b.localizedDescription()) + return false; + + if (a.isCancellation() != b.isCancellation()) + return false; + + return platformCompare(a, b); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/ResourceErrorBase.h b/Source/WebCore/platform/network/ResourceErrorBase.h new file mode 100644 index 0000000..a6b7c69 --- /dev/null +++ b/Source/WebCore/platform/network/ResourceErrorBase.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceErrorBase_h +#define ResourceErrorBase_h + +#include "PlatformString.h" + +namespace WebCore { + +class ResourceError; + +extern const char* const errorDomainWebKitInternal; // Used for errors that won't be exposed to clients. + +class ResourceErrorBase { +public: + // Makes a deep copy. Useful for when you need to use a ResourceError on another thread. + ResourceError copy() const; + + bool isNull() const { return m_isNull; } + + const String& domain() const { lazyInit(); return m_domain; } + int errorCode() const { lazyInit(); return m_errorCode; } + const String& failingURL() const { lazyInit(); return m_failingURL; } + const String& localizedDescription() const { lazyInit(); return m_localizedDescription; } + + void setIsCancellation(bool isCancellation) { m_isCancellation = isCancellation; } + bool isCancellation() const { return m_isCancellation; } + + static bool compare(const ResourceError&, const ResourceError&); + +protected: + ResourceErrorBase() + : m_errorCode(0) + , m_isNull(true) + , m_isCancellation(false) + { + } + + ResourceErrorBase(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) + : m_domain(domain) + , m_errorCode(errorCode) + , m_failingURL(failingURL) + , m_localizedDescription(localizedDescription) + , m_isNull(false) + , m_isCancellation(false) + { + } + + void lazyInit() const; + + // The ResourceError subclass may "shadow" this method to lazily initialize platform specific fields + void platformLazyInit() {} + + // The ResourceError subclass may "shadow" this method to compare platform specific fields + static bool platformCompare(const ResourceError&, const ResourceError&) { return true; } + + String m_domain; + int m_errorCode; + String m_failingURL; + String m_localizedDescription; + bool m_isNull; + bool m_isCancellation; +}; + +inline bool operator==(const ResourceError& a, const ResourceError& b) { return ResourceErrorBase::compare(a, b); } +inline bool operator!=(const ResourceError& a, const ResourceError& b) { return !(a == b); } + +} // namespace WebCore + +#endif // ResourceErrorBase_h diff --git a/Source/WebCore/platform/network/ResourceHandle.cpp b/Source/WebCore/platform/network/ResourceHandle.cpp new file mode 100644 index 0000000..9910ac1 --- /dev/null +++ b/Source/WebCore/platform/network/ResourceHandle.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2004, 2006, 2007, 2008, 2009, 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 COMPUTER, 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 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 "ResourceHandle.h" +#include "ResourceHandleInternal.h" + +#include "BlobRegistry.h" +#include "DNS.h" +#include "Logging.h" +#include "ResourceHandleClient.h" +#include "Timer.h" +#include <algorithm> + +namespace WebCore { + +static bool shouldForceContentSniffing; + +ResourceHandle::ResourceHandle(const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) + : d(new ResourceHandleInternal(this, request, client, defersLoading, shouldContentSniff && shouldContentSniffURL(request.url()))) +{ + if (!request.url().isValid()) { + scheduleFailure(InvalidURLFailure); + return; + } + + if (!portAllowed(request.url())) { + scheduleFailure(BlockedFailure); + return; + } +} + +PassRefPtr<ResourceHandle> ResourceHandle::create(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) +{ +#if ENABLE(BLOB) + if (request.url().protocolIs("blob")) { + PassRefPtr<ResourceHandle> handle = blobRegistry().createResourceHandle(request, client); + if (handle) + return handle; + } +#endif + + RefPtr<ResourceHandle> newHandle(adoptRef(new ResourceHandle(request, client, defersLoading, shouldContentSniff))); + + if (newHandle->d->m_scheduledFailureType != NoFailure) + return newHandle.release(); + + if (newHandle->start(context)) + return newHandle.release(); + + return 0; +} + +void ResourceHandle::scheduleFailure(FailureType type) +{ + d->m_scheduledFailureType = type; + d->m_failureTimer.startOneShot(0); +} + +void ResourceHandle::fireFailure(Timer<ResourceHandle>*) +{ + if (!client()) + return; + + switch (d->m_scheduledFailureType) { + case NoFailure: + ASSERT_NOT_REACHED(); + return; + case BlockedFailure: + d->m_scheduledFailureType = NoFailure; + client()->wasBlocked(this); + return; + case InvalidURLFailure: + d->m_scheduledFailureType = NoFailure; + client()->cannotShowURL(this); + return; + } + + ASSERT_NOT_REACHED(); +} + +ResourceHandleClient* ResourceHandle::client() const +{ + return d->m_client; +} + +void ResourceHandle::setClient(ResourceHandleClient* client) +{ + d->m_client = client; +} + +ResourceRequest& ResourceHandle::firstRequest() +{ + return d->m_firstRequest; +} + +const String& ResourceHandle::lastHTTPMethod() const +{ + return d->m_lastHTTPMethod; +} + +bool ResourceHandle::hasAuthenticationChallenge() const +{ + return !d->m_currentWebChallenge.isNull(); +} + +void ResourceHandle::clearAuthentication() +{ +#if PLATFORM(MAC) + d->m_currentMacChallenge = nil; +#endif + d->m_currentWebChallenge.nullify(); +} + +bool ResourceHandle::shouldContentSniff() const +{ + return d->m_shouldContentSniff; +} + +bool ResourceHandle::shouldContentSniffURL(const KURL& url) +{ +#if PLATFORM(MAC) + if (shouldForceContentSniffing) + return true; +#endif + // We shouldn't content sniff file URLs as their MIME type should be established via their extension. + return !url.protocolIs("file"); +} + +void ResourceHandle::forceContentSniffing() +{ + shouldForceContentSniffing = true; +} + +void ResourceHandle::setDefersLoading(bool defers) +{ + LOG(Network, "Handle %p setDefersLoading(%s)", this, defers ? "true" : "false"); + + ASSERT(d->m_defersLoading != defers); // Deferring is not counted, so calling setDefersLoading() repeatedly is likely to be in error. + d->m_defersLoading = defers; + + if (defers) { + ASSERT(d->m_failureTimer.isActive() == (d->m_scheduledFailureType != NoFailure)); + if (d->m_failureTimer.isActive()) + d->m_failureTimer.stop(); + } else if (d->m_scheduledFailureType != NoFailure) { + ASSERT(!d->m_failureTimer.isActive()); + d->m_failureTimer.startOneShot(0); + } + + platformSetDefersLoading(defers); +} + +#if !USE(SOUP) +void ResourceHandle::prepareForURL(const KURL& url) +{ + return prefetchDNS(url.host()); +} +#endif + +void ResourceHandle::cacheMetadata(const ResourceResponse&, const Vector<char>&) +{ + // Optionally implemented by platform. +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/ResourceHandle.h b/Source/WebCore/platform/network/ResourceHandle.h new file mode 100644 index 0000000..bb94b59 --- /dev/null +++ b/Source/WebCore/platform/network/ResourceHandle.h @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceHandle_h +#define ResourceHandle_h + +#include "AuthenticationChallenge.h" +#include "AuthenticationClient.h" +#include "HTTPHeaderMap.h" +#include "NetworkingContext.h" +#include "ThreadableLoader.h" +#include <wtf/OwnPtr.h> + +#if USE(SOUP) +typedef struct _SoupSession SoupSession; +#endif + +#if PLATFORM(CF) +typedef const struct __CFData * CFDataRef; +#endif + +#if PLATFORM(WIN) +typedef unsigned long DWORD; +typedef unsigned long DWORD_PTR; +typedef void* LPVOID; +typedef LPVOID HINTERNET; +#endif + + +#if PLATFORM(MAC) +#include <wtf/RetainPtr.h> +#ifdef __OBJC__ +@class NSData; +@class NSError; +@class NSURLConnection; +@class WebCoreResourceHandleAsDelegate; +#else +class NSData; +class NSError; +class NSURLConnection; +class WebCoreResourceHandleAsDelegate; +typedef struct objc_object *id; +#endif +#endif + +#if USE(CFNETWORK) +typedef struct _CFURLConnection* CFURLConnectionRef; +typedef int CFHTTPCookieStorageAcceptPolicy; +typedef struct OpaqueCFHTTPCookieStorage* CFHTTPCookieStorageRef; +#endif + +namespace WebCore { + +class AuthenticationChallenge; +class Credential; +class FormData; +class Frame; +class KURL; +class ProtectionSpace; +class ResourceError; +class ResourceHandleClient; +class ResourceHandleInternal; +class ResourceRequest; +class ResourceResponse; +class SchedulePair; +class SharedBuffer; + +template <typename T> class Timer; + +class ResourceHandle : public RefCounted<ResourceHandle> +#if PLATFORM(MAC) || USE(CFNETWORK) || USE(CURL) + , public AuthenticationClient +#endif + { +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); + + static void prepareForURL(const KURL&); + static bool willLoadFromCache(ResourceRequest&, Frame*); + static void cacheMetadata(const ResourceResponse&, const Vector<char>&); +#if PLATFORM(MAC) + static bool didSendBodyDataDelegateExists(); +#endif + + virtual ~ResourceHandle(); + +#if PLATFORM(MAC) || USE(CFNETWORK) + void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse); + bool shouldUseCredentialStorage(); +#endif +#if PLATFORM(MAC) || USE(CFNETWORK) || USE(CURL) + void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); + virtual void receivedCredential(const AuthenticationChallenge&, const Credential&); + virtual void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&); + virtual void receivedCancellation(const AuthenticationChallenge&); +#endif + +#if PLATFORM(MAC) + void didCancelAuthenticationChallenge(const AuthenticationChallenge&); +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + bool canAuthenticateAgainstProtectionSpace(const ProtectionSpace&); +#endif + NSURLConnection *connection() const; + WebCoreResourceHandleAsDelegate *delegate(); + void releaseDelegate(); + id releaseProxy(); + + void schedule(SchedulePair*); + void unschedule(SchedulePair*); +#elif USE(CFNETWORK) + CFURLConnectionRef connection() const; + CFURLConnectionRef releaseConnectionForDownload(); + static void setHostAllowsAnyHTTPSCertificate(const String&); + static void setClientCertificate(const String& host, CFDataRef); +#endif + +#if PLATFORM(WIN) && USE(CURL) + static void setHostAllowsAnyHTTPSCertificate(const String&); +#endif +#if PLATFORM(WIN) && USE(CURL) && PLATFORM(CF) + static void setClientCertificate(const String& host, CFDataRef); +#endif + + PassRefPtr<SharedBuffer> bufferedData(); + static bool supportsBufferedData(); + + bool shouldContentSniff() const; + static bool shouldContentSniffURL(const KURL&); + + static void forceContentSniffing(); + +#if USE(WININET) + void setSynchronousInternetHandle(HINTERNET); + void fileLoadTimer(Timer<ResourceHandle>*); + void onRedirect(); + bool onRequestComplete(); + static void CALLBACK internetStatusCallback(HINTERNET, DWORD_PTR, DWORD, LPVOID, DWORD); +#endif + +#if PLATFORM(QT) || USE(CURL) || USE(SOUP) || PLATFORM(ANDROID) + ResourceHandleInternal* getInternal() { return d.get(); } +#endif + +#if USE(SOUP) + static SoupSession* defaultSession(); +#endif + + // Used to work around the fact that you don't get any more NSURLConnection callbacks until you return from the one you're in. + static bool loadsBlocked(); + + bool hasAuthenticationChallenge() const; + void clearAuthentication(); + void cancel(); + + // The client may be 0, in which case no callbacks will be made. + ResourceHandleClient* client() const; + void setClient(ResourceHandleClient*); + + void setDefersLoading(bool); +#if PLATFORM(ANDROID) +// TODO: this needs upstreaming. + void pauseLoad(bool); +#endif + + ResourceRequest& firstRequest(); + const String& lastHTTPMethod() const; + + void fireFailure(Timer<ResourceHandle>*); + + 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); + + bool start(NetworkingContext*); + + virtual void refAuthenticationClient() { ref(); } + virtual void derefAuthenticationClient() { deref(); } + +#if PLATFORM(MAC) + void createNSURLConnection(id delegate, bool shouldUseCredentialStorage, bool shouldContentSniff); +#elif PLATFORM(CF) + void createCFURLConnection(bool shouldUseCredentialStorage, bool shouldContentSniff); +#endif + + friend class ResourceHandleInternal; + OwnPtr<ResourceHandleInternal> d; +}; + +} + +#endif // ResourceHandle_h diff --git a/Source/WebCore/platform/network/ResourceHandleClient.h b/Source/WebCore/platform/network/ResourceHandleClient.h new file mode 100644 index 0000000..d9350ee --- /dev/null +++ b/Source/WebCore/platform/network/ResourceHandleClient.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceHandleClient_h +#define ResourceHandleClient_h + +#include <wtf/CurrentTime.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +#if USE(CFNETWORK) +#include <ConditionalMacros.h> +#include <CFNetwork/CFURLCachePriv.h> +#include <CFNetwork/CFURLResponsePriv.h> +#endif + +#if PLATFORM(MAC) +#ifdef __OBJC__ +@class NSCachedURLResponse; +#else +class NSCachedURLResponse; +#endif +#endif + +namespace WebCore { + class AsyncFileStream; + class AuthenticationChallenge; + class Credential; + class FileStreamClient; + class KURL; + class ProtectionSpace; + class ResourceHandle; + class ResourceError; + class ResourceRequest; + class ResourceResponse; + + enum CacheStoragePolicy { + StorageAllowed, + StorageAllowedInMemoryOnly, + StorageNotAllowed, + }; + + class ResourceHandleClient { + public: + virtual ~ResourceHandleClient() { } + + // request may be modified + virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/) { } + virtual void didSendData(ResourceHandle*, unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/) { } + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&) { } + virtual void didReceiveData(ResourceHandle*, const char*, int, int /*lengthReceived*/) { } + virtual void didReceiveCachedMetadata(ResourceHandle*, const char*, int) { } + virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/) { } + virtual void didFail(ResourceHandle*, const ResourceError&) { } + virtual void wasBlocked(ResourceHandle*) { } + virtual void cannotShowURL(ResourceHandle*) { } + + virtual void willCacheResponse(ResourceHandle*, CacheStoragePolicy&) { } + + virtual bool shouldUseCredentialStorage(ResourceHandle*) { return false; } + virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge&) { } + virtual void didCancelAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge&) { } +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + virtual bool canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&) { return false; } +#endif + virtual void receivedCancellation(ResourceHandle*, const AuthenticationChallenge&) { } + +#if PLATFORM(MAC) + virtual NSCachedURLResponse* willCacheResponse(ResourceHandle*, NSCachedURLResponse* response) { return response; } + virtual void willStopBufferingData(ResourceHandle*, const char*, int) { } +#endif +#if USE(CFNETWORK) + virtual bool shouldCacheResponse(ResourceHandle*, CFCachedURLResponseRef response) { return true; } +#endif +#if ENABLE(BLOB) + virtual AsyncFileStream* createAsyncFileStream(FileStreamClient*) { return 0; } +#endif + }; + +} + +#endif diff --git a/Source/WebCore/platform/network/ResourceHandleInternal.h b/Source/WebCore/platform/network/ResourceHandleInternal.h new file mode 100644 index 0000000..d833e32 --- /dev/null +++ b/Source/WebCore/platform/network/ResourceHandleInternal.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceHandleInternal_h +#define ResourceHandleInternal_h + +#include "ResourceHandle.h" +#include "ResourceRequest.h" +#include "AuthenticationChallenge.h" +#include "Timer.h" + +#if USE(CFNETWORK) +#include <CFNetwork/CFURLConnectionPriv.h> +#endif + +#if USE(WININET) || (USE(CURL) && PLATFORM(WIN)) +#include <winsock2.h> +#include <windows.h> +#endif + +#if USE(CURL) +#include <curl/curl.h> +#include "FormDataStreamCurl.h" +#endif + +#if USE(SOUP) +#include "soup-requester.h" +#include <GRefPtr.h> +#include <libsoup/soup.h> +class Frame; +#endif + +#if PLATFORM(QT) +class QWebNetworkJob; +namespace WebCore { +class QNetworkReplyHandler; +} +#endif + +#if PLATFORM(MAC) +#ifdef __OBJC__ +@class NSURLAuthenticationChallenge; +@class NSURLConnection; +#else +class NSURLAuthenticationChallenge; +class NSURLConnection; +#endif +#endif + +#if PLATFORM(ANDROID) +#include "ResourceLoaderAndroid.h" +#endif + +// The allocations and releases in ResourceHandleInternal are +// Cocoa-exception-free (either simple Foundation classes or +// WebCoreResourceLoaderImp which avoids doing work in dealloc). + +namespace WebCore { + class ResourceHandleClient; + + class ResourceHandleInternal : public Noncopyable { + public: + ResourceHandleInternal(ResourceHandle* loader, const ResourceRequest& request, ResourceHandleClient* c, bool defersLoading, bool shouldContentSniff) + : m_client(c) + , m_firstRequest(request) + , m_lastHTTPMethod(request.httpMethod()) + , status(0) + , m_defersLoading(defersLoading) + , m_shouldContentSniff(shouldContentSniff) +#if USE(CFNETWORK) + , m_connection(0) +#endif +#if USE(WININET) + , m_fileLoadTimer(loader, &ResourceHandle::fileLoadTimer) + , m_internetHandle(0) + , m_connectHandle(0) + , m_requestHandle(0) + , m_sentEndRequest(false) + , m_bytesRemainingToWrite(0) + , m_loadSynchronously(false) + , m_hasReceivedResponse(false) +#endif +#if USE(CURL) + , m_handle(0) + , m_url(0) + , m_customHeaders(0) + , m_cancelled(false) + , m_formDataStream(loader) +#endif +#if USE(SOUP) + , m_cancelled(false) + , m_buffer(0) + , m_total(0) + , m_idleHandler(0) + , m_gotChunkHandler(0) +#endif +#if PLATFORM(QT) + , m_job(0) +#endif +#if PLATFORM(MAC) + , m_startWhenScheduled(false) + , m_needsSiteSpecificQuirks(false) + , m_currentMacChallenge(nil) +#endif + , m_scheduledFailureType(ResourceHandle::NoFailure) + , m_failureTimer(loader, &ResourceHandle::fireFailure) + { + const KURL& url = m_firstRequest.url(); + m_user = url.user(); + m_pass = url.pass(); + m_firstRequest.removeCredentials(); +#if USE(SOUP) + m_requester = adoptGRef(webkit_soup_requester_new()); +#endif + } + + ~ResourceHandleInternal(); + + ResourceHandleClient* client() { return m_client; } + ResourceHandleClient* m_client; + + ResourceRequest m_firstRequest; + String m_lastHTTPMethod; + + // Suggested credentials for the current redirection step. + String m_user; + String m_pass; + + Credential m_initialCredential; + + int status; + + bool m_defersLoading; + bool m_shouldContentSniff; +#if USE(CFNETWORK) + RetainPtr<CFURLConnectionRef> m_connection; +#elif PLATFORM(MAC) + RetainPtr<NSURLConnection> m_connection; + RetainPtr<WebCoreResourceHandleAsDelegate> m_delegate; + RetainPtr<id> m_proxy; + bool m_startWhenScheduled; + bool m_needsSiteSpecificQuirks; +#endif +#if USE(WININET) + Timer<ResourceHandle> m_fileLoadTimer; + HINTERNET m_internetHandle; + HINTERNET m_connectHandle; + HINTERNET m_requestHandle; + bool m_sentEndRequest; + Vector<char> m_formData; + size_t m_bytesRemainingToWrite; + bool m_loadSynchronously; + bool m_hasReceivedResponse; + String m_redirectUrl; +#endif +#if USE(CURL) + CURL* m_handle; + char* m_url; + struct curl_slist* m_customHeaders; + ResourceResponse m_response; + bool m_cancelled; + + FormDataStream m_formDataStream; + Vector<char> m_postBytes; +#endif +#if USE(SOUP) + GRefPtr<SoupMessage> m_soupMessage; + ResourceResponse m_response; + bool m_cancelled; + GRefPtr<WebKitSoupRequest> m_soupRequest; + GRefPtr<WebKitSoupRequester> m_requester; + GRefPtr<GInputStream> m_inputStream; + GRefPtr<GCancellable> m_cancellable; + char* m_buffer; + gsize m_total; + guint m_idleHandler; + RefPtr<NetworkingContext> m_context; + gulong m_gotChunkHandler; +#endif +#if PLATFORM(QT) + QNetworkReplyHandler* m_job; + RefPtr<NetworkingContext> m_context; +#endif + +#if PLATFORM(MAC) + // We need to keep a reference to the original challenge to be able to cancel it. + // It is almost identical to m_currentWebChallenge.nsURLAuthenticationChallenge(), but has a different sender. + NSURLAuthenticationChallenge *m_currentMacChallenge; +#endif +#if PLATFORM(ANDROID) + RefPtr<ResourceLoaderAndroid> m_loader; +#endif + AuthenticationChallenge m_currentWebChallenge; + + ResourceHandle::FailureType m_scheduledFailureType; + Timer<ResourceHandle> m_failureTimer; + }; + +} // namespace WebCore + +#endif // ResourceHandleInternal_h diff --git a/Source/WebCore/platform/network/ResourceLoadInfo.h b/Source/WebCore/platform/network/ResourceLoadInfo.h new file mode 100644 index 0000000..a1b958c --- /dev/null +++ b/Source/WebCore/platform/network/ResourceLoadInfo.h @@ -0,0 +1,46 @@ +/* + * 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 ResourceLoadInfo_h +#define ResourceLoadInfo_h + +#include "HTTPHeaderMap.h" + +namespace WebCore { + +struct ResourceLoadInfo : RefCounted<ResourceLoadInfo> { + ResourceLoadInfo() + : httpStatusCode(0) { } + + int httpStatusCode; + String httpStatusText; + HTTPHeaderMap requestHeaders; + HTTPHeaderMap responseHeaders; +}; + +} + +#endif diff --git a/Source/WebCore/platform/network/ResourceLoadPriority.h b/Source/WebCore/platform/network/ResourceLoadPriority.h new file mode 100644 index 0000000..1c9d5d2 --- /dev/null +++ b/Source/WebCore/platform/network/ResourceLoadPriority.h @@ -0,0 +1,45 @@ +/* + * 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 ResourceLoadPriority_h +#define ResourceLoadPriority_h + +namespace WebCore { + +enum ResourceLoadPriority { + ResourceLoadPriorityVeryLow, + ResourceLoadPriorityLow, + ResourceLoadPriorityMedium, + ResourceLoadPriorityHigh, + ResourceLoadPriorityLowest = ResourceLoadPriorityVeryLow, + ResourceLoadPriorityHighest = ResourceLoadPriorityHigh, + // The unresolved priority is here for the convenience of the clients. It should not be passed to the ResourceLoadScheduler. + ResourceLoadPriorityUnresolved, +}; + +} + +#endif + diff --git a/Source/WebCore/platform/network/ResourceLoadTiming.h b/Source/WebCore/platform/network/ResourceLoadTiming.h new file mode 100644 index 0000000..269fad0 --- /dev/null +++ b/Source/WebCore/platform/network/ResourceLoadTiming.h @@ -0,0 +1,114 @@ +/* + * 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 ResourceLoadTiming_h +#define ResourceLoadTiming_h + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class ResourceLoadTiming : public RefCounted<ResourceLoadTiming> { +public: + static PassRefPtr<ResourceLoadTiming> create() + { + return adoptRef(new ResourceLoadTiming); + } + + PassRefPtr<ResourceLoadTiming> deepCopy() + { + RefPtr<ResourceLoadTiming> timing = create(); + timing->requestTime = requestTime; + timing->proxyStart = proxyStart; + timing->proxyEnd = proxyEnd; + timing->dnsStart = dnsStart; + timing->dnsEnd = dnsEnd; + timing->connectStart = connectStart; + timing->connectEnd = connectEnd; + timing->sendStart = sendStart; + timing->sendEnd = sendEnd; + timing->receiveHeadersEnd = receiveHeadersEnd; + timing->sslStart = sslStart; + timing->sslEnd = sslEnd; + return timing.release(); + } + + bool operator==(const ResourceLoadTiming& other) const + { + return requestTime == other.requestTime + && proxyStart == other.proxyStart + && proxyEnd == other.proxyEnd + && dnsStart == other.dnsStart + && dnsEnd == other.dnsEnd + && connectStart == other.connectStart + && connectEnd == other.connectEnd + && sendStart == other.sendStart + && sendEnd == other.sendEnd + && receiveHeadersEnd == other.receiveHeadersEnd + && sslStart == other.sslStart + && sslEnd == other.sslEnd; + } + + bool operator!=(const ResourceLoadTiming& other) const + { + return !(*this == other); + } + + double requestTime; + int proxyStart; + int proxyEnd; + int dnsStart; + int dnsEnd; + int connectStart; + int connectEnd; + int sendStart; + int sendEnd; + int receiveHeadersEnd; + int sslStart; + int sslEnd; + +private: + ResourceLoadTiming() + : requestTime(0) + , proxyStart(-1) + , proxyEnd(-1) + , dnsStart(-1) + , dnsEnd(-1) + , connectStart(-1) + , connectEnd(-1) + , sendStart(0) + , sendEnd(0) + , receiveHeadersEnd(0) + , sslStart(-1) + , sslEnd(-1) + { + } +}; + +} + +#endif diff --git a/Source/WebCore/platform/network/ResourceRequestBase.cpp b/Source/WebCore/platform/network/ResourceRequestBase.cpp new file mode 100644 index 0000000..ae8316a --- /dev/null +++ b/Source/WebCore/platform/network/ResourceRequestBase.cpp @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2009 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 COMPUTER, 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 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 "ResourceRequestBase.h" +#include "ResourceRequest.h" + +using namespace std; + +namespace WebCore { + +inline const ResourceRequest& ResourceRequestBase::asResourceRequest() const +{ + return *static_cast<const ResourceRequest*>(this); +} + +PassOwnPtr<ResourceRequest> ResourceRequestBase::adopt(PassOwnPtr<CrossThreadResourceRequestData> data) +{ + OwnPtr<ResourceRequest> request(new ResourceRequest()); + request->setURL(data->m_url); + request->setCachePolicy(data->m_cachePolicy); + request->setTimeoutInterval(data->m_timeoutInterval); + request->setFirstPartyForCookies(data->m_firstPartyForCookies); + request->setHTTPMethod(data->m_httpMethod); + request->setTargetType(data->m_targetType); + + request->updateResourceRequest(); + request->m_httpHeaderFields.adopt(data->m_httpHeaders.release()); + + size_t encodingCount = data->m_responseContentDispositionEncodingFallbackArray.size(); + if (encodingCount > 0) { + String encoding1 = data->m_responseContentDispositionEncodingFallbackArray[0]; + String encoding2; + String encoding3; + if (encodingCount > 1) { + encoding2 = data->m_responseContentDispositionEncodingFallbackArray[1]; + if (encodingCount > 2) + encoding3 = data->m_responseContentDispositionEncodingFallbackArray[2]; + } + ASSERT(encodingCount <= 3); + request->setResponseContentDispositionEncodingFallbackArray(encoding1, encoding2, encoding3); + } + request->setHTTPBody(data->m_httpBody); + request->setAllowCookies(data->m_allowCookies); + request->doPlatformAdopt(data); + return request.release(); +} + +PassOwnPtr<CrossThreadResourceRequestData> ResourceRequestBase::copyData() const +{ + OwnPtr<CrossThreadResourceRequestData> data(new CrossThreadResourceRequestData()); + data->m_url = url().copy(); + data->m_cachePolicy = cachePolicy(); + data->m_timeoutInterval = timeoutInterval(); + data->m_firstPartyForCookies = firstPartyForCookies().copy(); + data->m_httpMethod = httpMethod().crossThreadString(); + data->m_httpHeaders = httpHeaderFields().copyData(); + data->m_targetType = m_targetType; + + data->m_responseContentDispositionEncodingFallbackArray.reserveInitialCapacity(m_responseContentDispositionEncodingFallbackArray.size()); + size_t encodingArraySize = m_responseContentDispositionEncodingFallbackArray.size(); + for (size_t index = 0; index < encodingArraySize; ++index) { + data->m_responseContentDispositionEncodingFallbackArray.append(m_responseContentDispositionEncodingFallbackArray[index].crossThreadString()); + } + if (m_httpBody) + data->m_httpBody = m_httpBody->deepCopy(); + data->m_allowCookies = m_allowCookies; + return asResourceRequest().doPlatformCopyData(data.release()); +} + +bool ResourceRequestBase::isEmpty() const +{ + updateResourceRequest(); + + return m_url.isEmpty(); +} + +bool ResourceRequestBase::isNull() const +{ + updateResourceRequest(); + + return m_url.isNull(); +} + +const KURL& ResourceRequestBase::url() const +{ + updateResourceRequest(); + + return m_url; +} + +void ResourceRequestBase::setURL(const KURL& url) +{ + updateResourceRequest(); + + m_url = url; + + m_platformRequestUpdated = false; +} + +void ResourceRequestBase::removeCredentials() +{ + updateResourceRequest(); + + m_url.setUser(String()); + m_url.setPass(String()); + + m_platformRequestUpdated = false; +} + +ResourceRequestCachePolicy ResourceRequestBase::cachePolicy() const +{ + updateResourceRequest(); + + return m_cachePolicy; +} + +void ResourceRequestBase::setCachePolicy(ResourceRequestCachePolicy cachePolicy) +{ + updateResourceRequest(); + + m_cachePolicy = cachePolicy; + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + +double ResourceRequestBase::timeoutInterval() const +{ + updateResourceRequest(); + + return m_timeoutInterval; +} + +void ResourceRequestBase::setTimeoutInterval(double timeoutInterval) +{ + updateResourceRequest(); + + m_timeoutInterval = timeoutInterval; + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + +const KURL& ResourceRequestBase::firstPartyForCookies() const +{ + updateResourceRequest(); + + return m_firstPartyForCookies; +} + +void ResourceRequestBase::setFirstPartyForCookies(const KURL& firstPartyForCookies) +{ + updateResourceRequest(); + + m_firstPartyForCookies = firstPartyForCookies; + + m_platformRequestUpdated = false; +} + +const String& ResourceRequestBase::httpMethod() const +{ + updateResourceRequest(); + + return m_httpMethod; +} + +void ResourceRequestBase::setHTTPMethod(const String& httpMethod) +{ + updateResourceRequest(); + + m_httpMethod = httpMethod; + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + +const HTTPHeaderMap& ResourceRequestBase::httpHeaderFields() const +{ + updateResourceRequest(); + + return m_httpHeaderFields; +} + +String ResourceRequestBase::httpHeaderField(const AtomicString& name) const +{ + updateResourceRequest(); + + return m_httpHeaderFields.get(name); +} + +String ResourceRequestBase::httpHeaderField(const char* name) const +{ + updateResourceRequest(); + + return m_httpHeaderFields.get(name); +} + +void ResourceRequestBase::setHTTPHeaderField(const AtomicString& name, const String& value) +{ + updateResourceRequest(); + + m_httpHeaderFields.set(name, value); + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + +void ResourceRequestBase::setHTTPHeaderField(const char* name, const String& value) +{ + setHTTPHeaderField(AtomicString(name), value); +} + +void ResourceRequestBase::clearHTTPAuthorization() +{ + updateResourceRequest(); + + m_httpHeaderFields.remove("Authorization"); + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + +void ResourceRequestBase::clearHTTPReferrer() +{ + updateResourceRequest(); + + m_httpHeaderFields.remove("Referer"); + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + +void ResourceRequestBase::clearHTTPOrigin() +{ + updateResourceRequest(); + + m_httpHeaderFields.remove("Origin"); + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + +void ResourceRequestBase::setResponseContentDispositionEncodingFallbackArray(const String& encoding1, const String& encoding2, const String& encoding3) +{ + updateResourceRequest(); + + m_responseContentDispositionEncodingFallbackArray.clear(); + if (!encoding1.isNull()) + m_responseContentDispositionEncodingFallbackArray.append(encoding1); + if (!encoding2.isNull()) + m_responseContentDispositionEncodingFallbackArray.append(encoding2); + if (!encoding3.isNull()) + m_responseContentDispositionEncodingFallbackArray.append(encoding3); + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + +FormData* ResourceRequestBase::httpBody() const +{ + updateResourceRequest(); + + return m_httpBody.get(); +} + +void ResourceRequestBase::setHTTPBody(PassRefPtr<FormData> httpBody) +{ + updateResourceRequest(); + + m_httpBody = httpBody; + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + +bool ResourceRequestBase::allowCookies() const +{ + updateResourceRequest(); + + return m_allowCookies; +} + +void ResourceRequestBase::setAllowCookies(bool allowCookies) +{ + updateResourceRequest(); + + m_allowCookies = allowCookies; + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + +void ResourceRequestBase::addHTTPHeaderField(const AtomicString& name, const String& value) +{ + updateResourceRequest(); + pair<HTTPHeaderMap::iterator, bool> result = m_httpHeaderFields.add(name, value); + if (!result.second) + result.first->second += "," + value; + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + +void ResourceRequestBase::addHTTPHeaderFields(const HTTPHeaderMap& headerFields) +{ + HTTPHeaderMap::const_iterator end = headerFields.end(); + for (HTTPHeaderMap::const_iterator it = headerFields.begin(); it != end; ++it) + addHTTPHeaderField(it->first, it->second); +} + +bool equalIgnoringHeaderFields(const ResourceRequestBase& a, const ResourceRequestBase& b) +{ + if (a.url() != b.url()) + return false; + + if (a.cachePolicy() != b.cachePolicy()) + return false; + + if (a.timeoutInterval() != b.timeoutInterval()) + return false; + + if (a.firstPartyForCookies() != b.firstPartyForCookies()) + return false; + + if (a.httpMethod() != b.httpMethod()) + return false; + + if (a.allowCookies() != b.allowCookies()) + return false; + + FormData* formDataA = a.httpBody(); + FormData* formDataB = b.httpBody(); + + if (!formDataA) + return !formDataB; + if (!formDataB) + return !formDataA; + + if (*formDataA != *formDataB) + return false; + + return true; +} + +bool ResourceRequestBase::compare(const ResourceRequest& a, const ResourceRequest& b) +{ + if (!equalIgnoringHeaderFields(a, b)) + return false; + + if (a.httpHeaderFields() != b.httpHeaderFields()) + return false; + + return ResourceRequest::platformCompare(a, b); +} + +bool ResourceRequestBase::isConditional() const +{ + return (m_httpHeaderFields.contains("If-Match") || + m_httpHeaderFields.contains("If-Modified-Since") || + m_httpHeaderFields.contains("If-None-Match") || + m_httpHeaderFields.contains("If-Range") || + m_httpHeaderFields.contains("If-Unmodified-Since")); +} + +void ResourceRequestBase::updatePlatformRequest() const +{ + if (m_platformRequestUpdated) + return; + + const_cast<ResourceRequest&>(asResourceRequest()).doUpdatePlatformRequest(); + m_platformRequestUpdated = true; +} + +void ResourceRequestBase::updateResourceRequest() const +{ + if (m_resourceRequestUpdated) + return; + + const_cast<ResourceRequest&>(asResourceRequest()).doUpdateResourceRequest(); + m_resourceRequestUpdated = true; +} + +#if !PLATFORM(MAC) && !USE(CFNETWORK) && !USE(SOUP) && !PLATFORM(CHROMIUM) && !PLATFORM(ANDROID) && !PLATFORM(QT) +unsigned initializeMaximumHTTPConnectionCountPerHost() +{ + // This is used by the loader to control the number of issued parallel load requests. + // Four seems to be a common default in HTTP frameworks. + return 4; +} +#endif + +} diff --git a/Source/WebCore/platform/network/ResourceRequestBase.h b/Source/WebCore/platform/network/ResourceRequestBase.h new file mode 100644 index 0000000..5cb7ee3 --- /dev/null +++ b/Source/WebCore/platform/network/ResourceRequestBase.h @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * Copyright (C) 2009 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 COMPUTER, 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 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 ResourceRequestBase_h +#define ResourceRequestBase_h + +#include "FormData.h" +#include "KURL.h" +#include "HTTPHeaderMap.h" + +#include <wtf/OwnPtr.h> + +namespace WebCore { + + enum ResourceRequestCachePolicy { + UseProtocolCachePolicy, // normal load + ReloadIgnoringCacheData, // reload + ReturnCacheDataElseLoad, // back/forward or encoding change - allow stale data + ReturnCacheDataDontLoad, // results of a post - allow stale data and only use cache + }; + + const int unspecifiedTimeoutInterval = INT_MAX; + + class ResourceRequest; + struct CrossThreadResourceRequestData; + + // Do not use this type directly. Use ResourceRequest instead. + class ResourceRequestBase : public FastAllocBase { + public: + // The type of this ResourceRequest, based on how the resource will be used. + enum TargetType { + TargetIsMainFrame, + TargetIsSubframe, + TargetIsSubresource, // Resource is a generic subresource. (Generally a specific type should be specified) + TargetIsStyleSheet, + TargetIsScript, + TargetIsFontResource, + TargetIsImage, + TargetIsObject, + TargetIsMedia, + TargetIsWorker, + TargetIsSharedWorker, + TargetIsPrefetch, + }; + + static PassOwnPtr<ResourceRequest> adopt(PassOwnPtr<CrossThreadResourceRequestData>); + + // Gets a copy of the data suitable for passing to another thread. + PassOwnPtr<CrossThreadResourceRequestData> copyData() const; + + bool isNull() const; + bool isEmpty() const; + + const KURL& url() const; + void setURL(const KURL& url); + + void removeCredentials(); + + ResourceRequestCachePolicy cachePolicy() const; + void setCachePolicy(ResourceRequestCachePolicy cachePolicy); + + double timeoutInterval() const; + void setTimeoutInterval(double timeoutInterval); + + const KURL& firstPartyForCookies() const; + void setFirstPartyForCookies(const KURL& firstPartyForCookies); + + const String& httpMethod() const; + void setHTTPMethod(const String& httpMethod); + + const HTTPHeaderMap& httpHeaderFields() const; + String httpHeaderField(const AtomicString& name) const; + String httpHeaderField(const char* name) const; + void setHTTPHeaderField(const AtomicString& name, const String& value); + void setHTTPHeaderField(const char* name, const String& value); + void addHTTPHeaderField(const AtomicString& name, const String& value); + void addHTTPHeaderFields(const HTTPHeaderMap& headerFields); + + void clearHTTPAuthorization(); + + String httpContentType() const { return httpHeaderField("Content-Type"); } + void setHTTPContentType(const String& httpContentType) { setHTTPHeaderField("Content-Type", httpContentType); } + + String httpReferrer() const { return httpHeaderField("Referer"); } + void setHTTPReferrer(const String& httpReferrer) { setHTTPHeaderField("Referer", httpReferrer); } + void clearHTTPReferrer(); + + String httpOrigin() const { return httpHeaderField("Origin"); } + void setHTTPOrigin(const String& httpOrigin) { setHTTPHeaderField("Origin", httpOrigin); } + void clearHTTPOrigin(); + + String httpUserAgent() const { return httpHeaderField("User-Agent"); } + void setHTTPUserAgent(const String& httpUserAgent) { setHTTPHeaderField("User-Agent", httpUserAgent); } + + String httpAccept() const { return httpHeaderField("Accept"); } + void setHTTPAccept(const String& httpAccept) { setHTTPHeaderField("Accept", httpAccept); } + + void setResponseContentDispositionEncodingFallbackArray(const String& encoding1, const String& encoding2 = String(), const String& encoding3 = String()); + + FormData* httpBody() const; + void setHTTPBody(PassRefPtr<FormData> httpBody); + + bool allowCookies() const; + void setAllowCookies(bool allowCookies); + + bool isConditional() const; + + // Whether the associated ResourceHandleClient needs to be notified of + // upload progress made for that resource. + bool reportUploadProgress() const { return m_reportUploadProgress; } + void setReportUploadProgress(bool reportUploadProgress) { m_reportUploadProgress = reportUploadProgress; } + + // Whether the timing information should be collected for the request. + 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() + : m_resourceRequestUpdated(false) + , m_platformRequestUpdated(true) + , m_reportUploadProgress(false) + , m_reportLoadTiming(false) + , m_reportRawHeaders(false) + , m_targetType(TargetIsSubresource) + { + } + + ResourceRequestBase(const KURL& url, ResourceRequestCachePolicy policy) + : m_url(url) + , m_cachePolicy(policy) + , m_timeoutInterval(unspecifiedTimeoutInterval) + , m_httpMethod("GET") + , m_allowCookies(true) + , m_resourceRequestUpdated(true) + , m_platformRequestUpdated(false) + , m_reportUploadProgress(false) + , m_reportLoadTiming(false) + , m_reportRawHeaders(false) + , m_targetType(TargetIsSubresource) + { + } + + 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; + double m_timeoutInterval; + KURL m_firstPartyForCookies; + String m_httpMethod; + HTTPHeaderMap m_httpHeaderFields; + Vector<String> m_responseContentDispositionEncodingFallbackArray; + RefPtr<FormData> m_httpBody; + bool m_allowCookies; + mutable bool m_resourceRequestUpdated; + mutable bool m_platformRequestUpdated; + bool m_reportUploadProgress; + bool m_reportLoadTiming; + bool m_reportRawHeaders; + TargetType m_targetType; + + private: + const ResourceRequest& asResourceRequest() const; + }; + + bool equalIgnoringHeaderFields(const ResourceRequestBase&, const ResourceRequestBase&); + + 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 CrossThreadResourceRequestDataBase : Noncopyable { + KURL m_url; + + ResourceRequestCachePolicy m_cachePolicy; + double m_timeoutInterval; + KURL m_firstPartyForCookies; + + String m_httpMethod; + OwnPtr<CrossThreadHTTPHeaderMapData> m_httpHeaders; + Vector<String> m_responseContentDispositionEncodingFallbackArray; + RefPtr<FormData> m_httpBody; + bool m_allowCookies; + ResourceRequestBase::TargetType m_targetType; + }; + + unsigned initializeMaximumHTTPConnectionCountPerHost(); + +} // namespace WebCore + +#endif // ResourceRequestBase_h diff --git a/Source/WebCore/platform/network/ResourceResponseBase.cpp b/Source/WebCore/platform/network/ResourceResponseBase.cpp new file mode 100644 index 0000000..55eac76 --- /dev/null +++ b/Source/WebCore/platform/network/ResourceResponseBase.cpp @@ -0,0 +1,664 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 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 COMPUTER, 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 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 "ResourceResponseBase.h" + +#include "HTTPParsers.h" +#include "ResourceResponse.h" +#include <wtf/CurrentTime.h> +#include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> + +using namespace std; + +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) + , m_lastModifiedDate(0) + , m_wasCached(false) + , m_connectionID(0) + , m_connectionReused(false) + , m_isNull(true) + , m_haveParsedCacheControlHeader(false) + , m_haveParsedAgeHeader(false) + , m_haveParsedDateHeader(false) + , m_haveParsedExpiresHeader(false) + , m_haveParsedLastModifiedHeader(false) + , m_cacheControlContainsNoCache(false) + , m_cacheControlContainsNoStore(false) + , m_cacheControlContainsMustRevalidate(false) + , m_cacheControlMaxAge(0.0) + , m_age(0.0) + , m_date(0.0) + , m_expires(0.0) + , m_lastModified(0.0) +{ +} + +ResourceResponseBase::ResourceResponseBase(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) + : m_url(url) + , m_mimeType(mimeType) + , m_expectedContentLength(expectedLength) + , m_textEncodingName(textEncodingName) + , m_suggestedFilename(filename) + , m_httpStatusCode(0) + , m_lastModifiedDate(0) + , m_wasCached(false) + , m_connectionID(0) + , m_connectionReused(false) + , m_isNull(false) + , m_haveParsedCacheControlHeader(false) + , m_haveParsedAgeHeader(false) + , m_haveParsedDateHeader(false) + , m_haveParsedExpiresHeader(false) + , m_haveParsedLastModifiedHeader(false) + , m_cacheControlContainsNoCache(false) + , m_cacheControlContainsNoStore(false) + , m_cacheControlContainsMustRevalidate(false) + , m_cacheControlMaxAge(0.0) + , m_age(0.0) + , m_date(0.0) + , m_expires(0.0) + , m_lastModified(0.0) +{ +} + +PassOwnPtr<ResourceResponse> ResourceResponseBase::adopt(PassOwnPtr<CrossThreadResourceResponseData> data) +{ + OwnPtr<ResourceResponse> response(new ResourceResponse()); + response->setURL(data->m_url); + response->setMimeType(data->m_mimeType); + response->setExpectedContentLength(data->m_expectedContentLength); + response->setTextEncodingName(data->m_textEncodingName); + response->setSuggestedFilename(data->m_suggestedFilename); + + response->setHTTPStatusCode(data->m_httpStatusCode); + response->setHTTPStatusText(data->m_httpStatusText); + + response->lazyInit(); + 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(); +} + +PassOwnPtr<CrossThreadResourceResponseData> ResourceResponseBase::copyData() const +{ + OwnPtr<CrossThreadResourceResponseData> data(new CrossThreadResourceResponseData()); + data->m_url = url().copy(); + data->m_mimeType = mimeType().crossThreadString(); + data->m_expectedContentLength = expectedContentLength(); + data->m_textEncodingName = textEncodingName().crossThreadString(); + data->m_suggestedFilename = suggestedFilename().crossThreadString(); + data->m_httpStatusCode = httpStatusCode(); + data->m_httpStatusText = httpStatusText().crossThreadString(); + data->m_httpHeaders = httpHeaderFields().copyData(); + data->m_lastModifiedDate = lastModifiedDate(); + if (m_resourceLoadTiming) + data->m_resourceLoadTiming = m_resourceLoadTiming->deepCopy(); + return asResourceResponse().doPlatformCopyData(data.release()); +} + +bool ResourceResponseBase::isHTTP() const +{ + lazyInit(); + + String protocol = m_url.protocol(); + + return equalIgnoringCase(protocol, "http") || equalIgnoringCase(protocol, "https"); +} + +const KURL& ResourceResponseBase::url() const +{ + lazyInit(); + + return m_url; +} + +void ResourceResponseBase::setURL(const KURL& url) +{ + lazyInit(); + m_isNull = false; + + m_url = url; +} + +const String& ResourceResponseBase::mimeType() const +{ + lazyInit(); + + return m_mimeType; +} + +void ResourceResponseBase::setMimeType(const String& mimeType) +{ + lazyInit(); + m_isNull = false; + + m_mimeType = mimeType; +} + +long long ResourceResponseBase::expectedContentLength() const +{ + lazyInit(); + + return m_expectedContentLength; +} + +void ResourceResponseBase::setExpectedContentLength(long long expectedContentLength) +{ + lazyInit(); + m_isNull = false; + + m_expectedContentLength = expectedContentLength; +} + +const String& ResourceResponseBase::textEncodingName() const +{ + lazyInit(); + + return m_textEncodingName; +} + +void ResourceResponseBase::setTextEncodingName(const String& encodingName) +{ + lazyInit(); + m_isNull = false; + + m_textEncodingName = encodingName; +} + +// FIXME should compute this on the fly +const String& ResourceResponseBase::suggestedFilename() const +{ + lazyInit(); + + return m_suggestedFilename; +} + +void ResourceResponseBase::setSuggestedFilename(const String& suggestedName) +{ + lazyInit(); + m_isNull = false; + + m_suggestedFilename = suggestedName; +} + +int ResourceResponseBase::httpStatusCode() const +{ + lazyInit(); + + return m_httpStatusCode; +} + +void ResourceResponseBase::setHTTPStatusCode(int statusCode) +{ + lazyInit(); + + m_httpStatusCode = statusCode; +} + +const String& ResourceResponseBase::httpStatusText() const +{ + lazyInit(); + + return m_httpStatusText; +} + +void ResourceResponseBase::setHTTPStatusText(const String& statusText) +{ + lazyInit(); + + m_httpStatusText = statusText; +} + +String ResourceResponseBase::httpHeaderField(const AtomicString& name) const +{ + lazyInit(); + + return m_httpHeaderFields.get(name); +} + +String ResourceResponseBase::httpHeaderField(const char* name) const +{ + lazyInit(); + + return m_httpHeaderFields.get(name); +} + +void ResourceResponseBase::setHTTPHeaderField(const AtomicString& name, const String& value) +{ + lazyInit(); + + DEFINE_STATIC_LOCAL(const AtomicString, ageHeader, ("age")); + DEFINE_STATIC_LOCAL(const AtomicString, cacheControlHeader, ("cache-control")); + DEFINE_STATIC_LOCAL(const AtomicString, dateHeader, ("date")); + DEFINE_STATIC_LOCAL(const AtomicString, expiresHeader, ("expires")); + DEFINE_STATIC_LOCAL(const AtomicString, lastModifiedHeader, ("last-modified")); + DEFINE_STATIC_LOCAL(const AtomicString, pragmaHeader, ("pragma")); + if (equalIgnoringCase(name, ageHeader)) + m_haveParsedAgeHeader = false; + else if (equalIgnoringCase(name, cacheControlHeader) || equalIgnoringCase(name, pragmaHeader)) + m_haveParsedCacheControlHeader = false; + else if (equalIgnoringCase(name, dateHeader)) + m_haveParsedDateHeader = false; + else if (equalIgnoringCase(name, expiresHeader)) + m_haveParsedExpiresHeader = false; + else if (equalIgnoringCase(name, lastModifiedHeader)) + m_haveParsedLastModifiedHeader = false; + + m_httpHeaderFields.set(name, value); +} + +const HTTPHeaderMap& ResourceResponseBase::httpHeaderFields() const +{ + lazyInit(); + + return m_httpHeaderFields; +} + +void ResourceResponseBase::parseCacheControlDirectives() const +{ + ASSERT(!m_haveParsedCacheControlHeader); + + lazyInit(); + + m_haveParsedCacheControlHeader = true; + + m_cacheControlContainsMustRevalidate = false; + m_cacheControlContainsNoCache = false; + m_cacheControlMaxAge = numeric_limits<double>::quiet_NaN(); + + DEFINE_STATIC_LOCAL(const AtomicString, cacheControlString, ("cache-control")); + DEFINE_STATIC_LOCAL(const AtomicString, noCacheDirective, ("no-cache")); + DEFINE_STATIC_LOCAL(const AtomicString, noStoreDirective, ("no-store")); + DEFINE_STATIC_LOCAL(const AtomicString, mustRevalidateDirective, ("must-revalidate")); + DEFINE_STATIC_LOCAL(const AtomicString, maxAgeDirective, ("max-age")); + + String cacheControlValue = m_httpHeaderFields.get(cacheControlString); + if (!cacheControlValue.isEmpty()) { + Vector<pair<String, String> > directives; + parseCacheHeader(cacheControlValue, directives); + + size_t directivesSize = directives.size(); + for (size_t i = 0; i < directivesSize; ++i) { + // RFC2616 14.9.1: A no-cache directive with a value is only meaningful for proxy caches. + // It should be ignored by a browser level cache. + if (equalIgnoringCase(directives[i].first, noCacheDirective) && directives[i].second.isEmpty()) + m_cacheControlContainsNoCache = true; + else if (equalIgnoringCase(directives[i].first, noStoreDirective)) + m_cacheControlContainsNoStore = true; + else if (equalIgnoringCase(directives[i].first, mustRevalidateDirective)) + m_cacheControlContainsMustRevalidate = true; + else if (equalIgnoringCase(directives[i].first, maxAgeDirective)) { + bool ok; + double maxAge = directives[i].second.toDouble(&ok); + if (ok) + m_cacheControlMaxAge = maxAge; + } + } + } + + if (!m_cacheControlContainsNoCache) { + // Handle Pragma: no-cache + // This is deprecated and equivalent to Cache-control: no-cache + // Don't bother tokenizing the value, it is not important + DEFINE_STATIC_LOCAL(const AtomicString, pragmaHeader, ("pragma")); + String pragmaValue = m_httpHeaderFields.get(pragmaHeader); + m_cacheControlContainsNoCache = pragmaValue.lower().contains(noCacheDirective); + } +} + +bool ResourceResponseBase::cacheControlContainsNoCache() const +{ + if (!m_haveParsedCacheControlHeader) + parseCacheControlDirectives(); + return m_cacheControlContainsNoCache; +} + +bool ResourceResponseBase::cacheControlContainsNoStore() const +{ + if (!m_haveParsedCacheControlHeader) + parseCacheControlDirectives(); + return m_cacheControlContainsNoStore; +} + +bool ResourceResponseBase::cacheControlContainsMustRevalidate() const +{ + if (!m_haveParsedCacheControlHeader) + parseCacheControlDirectives(); + return m_cacheControlContainsMustRevalidate; +} + +double ResourceResponseBase::cacheControlMaxAge() const +{ + if (!m_haveParsedCacheControlHeader) + parseCacheControlDirectives(); + return m_cacheControlMaxAge; +} + +static double parseDateValueInHeader(const HTTPHeaderMap& headers, const AtomicString& headerName) +{ + String headerValue = headers.get(headerName); + if (headerValue.isEmpty()) + return std::numeric_limits<double>::quiet_NaN(); + // This handles all date formats required by RFC2616: + // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + double dateInMilliseconds = parseDate(headerValue); + if (!isfinite(dateInMilliseconds)) + return std::numeric_limits<double>::quiet_NaN(); + return dateInMilliseconds / 1000; +} + +double ResourceResponseBase::date() const +{ + lazyInit(); + + if (!m_haveParsedDateHeader) { + DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("date")); + m_date = parseDateValueInHeader(m_httpHeaderFields, headerName); + m_haveParsedDateHeader = true; + } + return m_date; +} + +double ResourceResponseBase::age() const +{ + lazyInit(); + + if (!m_haveParsedAgeHeader) { + DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("age")); + String headerValue = m_httpHeaderFields.get(headerName); + bool ok; + m_age = headerValue.toDouble(&ok); + if (!ok) + m_age = std::numeric_limits<double>::quiet_NaN(); + m_haveParsedAgeHeader = true; + } + return m_age; +} + +double ResourceResponseBase::expires() const +{ + lazyInit(); + + if (!m_haveParsedExpiresHeader) { + DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("expires")); + m_expires = parseDateValueInHeader(m_httpHeaderFields, headerName); + m_haveParsedExpiresHeader = true; + } + return m_expires; +} + +double ResourceResponseBase::lastModified() const +{ + lazyInit(); + + if (!m_haveParsedLastModifiedHeader) { + DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("last-modified")); + m_lastModified = parseDateValueInHeader(m_httpHeaderFields, headerName); + m_haveParsedLastModifiedHeader = true; + } + return m_lastModified; +} + +bool ResourceResponseBase::isAttachment() const +{ + lazyInit(); + + DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("content-disposition")); + String value = m_httpHeaderFields.get(headerName); + size_t loc = value.find(';'); + if (loc != notFound) + value = value.left(loc); + value = value.stripWhiteSpace(); + DEFINE_STATIC_LOCAL(const AtomicString, attachmentString, ("attachment")); + return equalIgnoringCase(value, attachmentString); +} + +void ResourceResponseBase::setLastModifiedDate(time_t lastModifiedDate) +{ + lazyInit(); + + m_lastModifiedDate = lastModifiedDate; +} + +time_t ResourceResponseBase::lastModifiedDate() const +{ + lazyInit(); + + return m_lastModifiedDate; +} + +bool ResourceResponseBase::wasCached() const +{ + lazyInit(); + + return m_wasCached; +} + +void ResourceResponseBase::setWasCached(bool value) +{ + m_wasCached = value; +} + +bool ResourceResponseBase::connectionReused() const +{ + lazyInit(); + + return m_connectionReused; +} + +void ResourceResponseBase::setConnectionReused(bool connectionReused) +{ + lazyInit(); + + m_connectionReused = connectionReused; +} + +unsigned ResourceResponseBase::connectionID() const +{ + lazyInit(); + + return m_connectionID; +} + +void ResourceResponseBase::setConnectionID(unsigned connectionID) +{ + lazyInit(); + + m_connectionID = connectionID; +} + +ResourceLoadTiming* ResourceResponseBase::resourceLoadTiming() const +{ + lazyInit(); + + return m_resourceLoadTiming.get(); +} + +void ResourceResponseBase::setResourceLoadTiming(PassRefPtr<ResourceLoadTiming> resourceLoadTiming) +{ + lazyInit(); + + m_resourceLoadTiming = resourceLoadTiming; +} + +PassRefPtr<ResourceLoadInfo> ResourceResponseBase::resourceLoadInfo() const +{ + lazyInit(); + + return m_resourceLoadInfo.get(); +} + +void ResourceResponseBase::setResourceLoadInfo(PassRefPtr<ResourceLoadInfo> loadInfo) +{ + lazyInit(); + + m_resourceLoadInfo = loadInfo; +} + +void ResourceResponseBase::lazyInit() const +{ + const_cast<ResourceResponse*>(static_cast<const ResourceResponse*>(this))->platformLazyInit(); +} + +bool ResourceResponseBase::compare(const ResourceResponse& a, const ResourceResponse& b) +{ + if (a.isNull() != b.isNull()) + return false; + if (a.url() != b.url()) + return false; + if (a.mimeType() != b.mimeType()) + return false; + if (a.expectedContentLength() != b.expectedContentLength()) + return false; + if (a.textEncodingName() != b.textEncodingName()) + return false; + if (a.suggestedFilename() != b.suggestedFilename()) + return false; + if (a.httpStatusCode() != b.httpStatusCode()) + return false; + if (a.httpStatusText() != b.httpStatusText()) + return false; + if (a.httpHeaderFields() != b.httpHeaderFields()) + return false; + if (a.resourceLoadTiming() && b.resourceLoadTiming() && *a.resourceLoadTiming() == *b.resourceLoadTiming()) + return ResourceResponse::platformCompare(a, b); + if (a.resourceLoadTiming() != b.resourceLoadTiming()) + return false; + return ResourceResponse::platformCompare(a, b); +} + +static bool isCacheHeaderSeparator(UChar c) +{ + // See RFC 2616, Section 2.2 + switch (c) { + case '(': + case ')': + case '<': + case '>': + case '@': + case ',': + case ';': + case ':': + case '\\': + case '"': + case '/': + case '[': + case ']': + case '?': + case '=': + case '{': + case '}': + case ' ': + case '\t': + return true; + default: + return false; + } +} + +static bool isControlCharacter(UChar c) +{ + return c < ' ' || c == 127; +} + +static inline String trimToNextSeparator(const String& str) +{ + return str.substring(0, str.find(isCacheHeaderSeparator, 0)); +} + +static void parseCacheHeader(const String& header, Vector<pair<String, String> >& result) +{ + const String safeHeader = header.removeCharacters(isControlCharacter); + unsigned max = safeHeader.length(); + for (unsigned pos = 0; pos < max; /* pos incremented in loop */) { + size_t nextCommaPosition = safeHeader.find(',', pos); + size_t nextEqualSignPosition = safeHeader.find('=', pos); + if (nextEqualSignPosition != notFound && (nextEqualSignPosition < nextCommaPosition || nextCommaPosition == notFound)) { + // Get directive name, parse right hand side of equal sign, then add to map + String directive = trimToNextSeparator(safeHeader.substring(pos, nextEqualSignPosition - pos).stripWhiteSpace()); + pos += nextEqualSignPosition - pos + 1; + + String value = safeHeader.substring(pos, max - pos).stripWhiteSpace(); + if (value[0] == '"') { + // The value is a quoted string + size_t nextDoubleQuotePosition = value.find('"', 1); + if (nextDoubleQuotePosition != notFound) { + // Store the value as a quoted string without quotes + result.append(pair<String, String>(directive, value.substring(1, nextDoubleQuotePosition - 1).stripWhiteSpace())); + pos += (safeHeader.find('"', pos) - pos) + nextDoubleQuotePosition + 1; + // Move past next comma, if there is one + size_t nextCommaPosition2 = safeHeader.find(',', pos); + if (nextCommaPosition2 != notFound) + pos += nextCommaPosition2 - pos + 1; + else + return; // Parse error if there is anything left with no comma + } else { + // Parse error; just use the rest as the value + result.append(pair<String, String>(directive, trimToNextSeparator(value.substring(1, value.length() - 1).stripWhiteSpace()))); + return; + } + } else { + // The value is a token until the next comma + size_t nextCommaPosition2 = value.find(',', 0); + if (nextCommaPosition2 != notFound) { + // The value is delimited by the next comma + result.append(pair<String, String>(directive, trimToNextSeparator(value.substring(0, nextCommaPosition2).stripWhiteSpace()))); + pos += (safeHeader.find(',', pos) - pos) + 1; + } else { + // The rest is the value; no change to value needed + result.append(pair<String, String>(directive, trimToNextSeparator(value))); + return; + } + } + } else if (nextCommaPosition != notFound && (nextCommaPosition < nextEqualSignPosition || nextEqualSignPosition == notFound)) { + // Add directive to map with empty string as value + result.append(pair<String, String>(trimToNextSeparator(safeHeader.substring(pos, nextCommaPosition - pos).stripWhiteSpace()), "")); + pos += nextCommaPosition - pos + 1; + } else { + // Add last directive to map with empty string as value + result.append(pair<String, String>(trimToNextSeparator(safeHeader.substring(pos, max - pos).stripWhiteSpace()), "")); + return; + } + } +} + +} diff --git a/Source/WebCore/platform/network/ResourceResponseBase.h b/Source/WebCore/platform/network/ResourceResponseBase.h new file mode 100644 index 0000000..9c54bab --- /dev/null +++ b/Source/WebCore/platform/network/ResourceResponseBase.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 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 COMPUTER, 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 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 ResourceResponseBase_h +#define ResourceResponseBase_h + +#include "HTTPHeaderMap.h" +#include "KURL.h" +#include "ResourceLoadInfo.h" +#include "ResourceLoadTiming.h" + +#include <wtf/PassOwnPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class ResourceResponse; +struct CrossThreadResourceResponseData; + +// Do not use this class directly, use the class ResponseResponse instead +class ResourceResponseBase : public FastAllocBase { +public: + static PassOwnPtr<ResourceResponse> adopt(PassOwnPtr<CrossThreadResourceResponseData>); + + // Gets a copy of the data suitable for passing to another thread. + PassOwnPtr<CrossThreadResourceResponseData> copyData() const; + + bool isNull() const { return m_isNull; } + bool isHTTP() const; + + const KURL& url() const; + void setURL(const KURL& url); + + const String& mimeType() const; + void setMimeType(const String& mimeType); + + long long expectedContentLength() const; + void setExpectedContentLength(long long expectedContentLength); + + const String& textEncodingName() const; + void setTextEncodingName(const String& name); + + // FIXME should compute this on the fly + const String& suggestedFilename() const; + void setSuggestedFilename(const String&); + + int httpStatusCode() const; + void setHTTPStatusCode(int); + + const String& httpStatusText() const; + void setHTTPStatusText(const String&); + + String httpHeaderField(const AtomicString& name) const; + String httpHeaderField(const char* name) const; + void setHTTPHeaderField(const AtomicString& name, const String& value); + const HTTPHeaderMap& httpHeaderFields() const; + + bool isMultipart() const { return mimeType() == "multipart/x-mixed-replace"; } + + bool isAttachment() const; + + // FIXME: These are used by PluginStream on some platforms. Calculations may differ from just returning plain Last-odified header. + // Leaving it for now but this should go away in favor of generic solution. + void setLastModifiedDate(time_t); + time_t lastModifiedDate() const; + + // These functions return parsed values of the corresponding response headers. + // NaN means that the header was not present or had invalid value. + bool cacheControlContainsNoCache() const; + bool cacheControlContainsNoStore() const; + bool cacheControlContainsMustRevalidate() const; + double cacheControlMaxAge() const; + double date() const; + double age() const; + double expires() const; + double lastModified() const; + + unsigned connectionID() const; + void setConnectionID(unsigned); + + bool connectionReused() const; + void setConnectionReused(bool); + + bool wasCached() const; + void setWasCached(bool); + + ResourceLoadTiming* resourceLoadTiming() const; + void setResourceLoadTiming(PassRefPtr<ResourceLoadTiming>); + + PassRefPtr<ResourceLoadInfo> resourceLoadInfo() const; + void setResourceLoadInfo(PassRefPtr<ResourceLoadInfo>); + + // The ResourceResponse subclass may "shadow" this method to provide platform-specific memory usage information + unsigned memoryUsage() const + { + // average size, mostly due to URL and Header Map strings + return 1280; + } + + static bool compare(const ResourceResponse&, const ResourceResponse&); + +protected: + ResourceResponseBase(); + ResourceResponseBase(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename); + + void lazyInit() const; + + // The ResourceResponse subclass may "shadow" this method to lazily initialize platform specific fields + void platformLazyInit() { } + + // The ResourceResponse subclass may "shadow" this method to compare platform specific fields + static bool platformCompare(const ResourceResponse&, const ResourceResponse&) { return true; } + + KURL m_url; + String m_mimeType; + long long m_expectedContentLength; + String m_textEncodingName; + String m_suggestedFilename; + int m_httpStatusCode; + String m_httpStatusText; + HTTPHeaderMap m_httpHeaderFields; + time_t m_lastModifiedDate; + bool m_wasCached : 1; + unsigned m_connectionID; + bool m_connectionReused : 1; + RefPtr<ResourceLoadTiming> m_resourceLoadTiming; + RefPtr<ResourceLoadInfo> m_resourceLoadInfo; + + bool m_isNull : 1; + +private: + const ResourceResponse& asResourceResponse() const; + void parseCacheControlDirectives() const; + + mutable bool m_haveParsedCacheControlHeader : 1; + mutable bool m_haveParsedAgeHeader : 1; + mutable bool m_haveParsedDateHeader : 1; + mutable bool m_haveParsedExpiresHeader : 1; + mutable bool m_haveParsedLastModifiedHeader : 1; + + mutable bool m_cacheControlContainsNoCache : 1; + mutable bool m_cacheControlContainsNoStore : 1; + mutable bool m_cacheControlContainsMustRevalidate : 1; + mutable double m_cacheControlMaxAge; + + mutable double m_age; + mutable double m_date; + mutable double m_expires; + mutable double m_lastModified; +}; + +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 CrossThreadResourceResponseDataBase : Noncopyable { + KURL m_url; + String m_mimeType; + long long m_expectedContentLength; + String m_textEncodingName; + String m_suggestedFilename; + int m_httpStatusCode; + String m_httpStatusText; + OwnPtr<CrossThreadHTTPHeaderMapData> m_httpHeaders; + time_t m_lastModifiedDate; + RefPtr<ResourceLoadTiming> m_resourceLoadTiming; +}; + +} // namespace WebCore + +#endif // ResourceResponseBase_h diff --git a/Source/WebCore/platform/network/SocketStreamErrorBase.cpp b/Source/WebCore/platform/network/SocketStreamErrorBase.cpp new file mode 100644 index 0000000..bbb5d55 --- /dev/null +++ b/Source/WebCore/platform/network/SocketStreamErrorBase.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "SocketStreamError.h" + +namespace WebCore { + +SocketStreamError SocketStreamErrorBase::copy() const +{ + SocketStreamError errorCopy; + errorCopy.m_errorCode = m_errorCode; + return errorCopy; +} + +bool SocketStreamErrorBase::compare(const SocketStreamError& a, const SocketStreamError& b) +{ + if (a.isNull() && b.isNull()) + return true; + + if (a.isNull() || b.isNull()) + return false; + + if (a.errorCode() != b.errorCode()) + return false; + + if (a.failingURL() != b.failingURL()) + return false; + + if (a.localizedDescription() != b.localizedDescription()) + return false; + + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/SocketStreamErrorBase.h b/Source/WebCore/platform/network/SocketStreamErrorBase.h new file mode 100644 index 0000000..e6cc567 --- /dev/null +++ b/Source/WebCore/platform/network/SocketStreamErrorBase.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamErrorBase_h +#define SocketStreamErrorBase_h + +#include "PlatformString.h" + +namespace WebCore { + + class SocketStreamError; + + class SocketStreamErrorBase { + public: + // Makes a deep copy. Useful for when you need to use a SocketStreamError on another thread. + SocketStreamError copy() const; + + bool isNull() const { return m_isNull; } + + int errorCode() const { return m_errorCode; } + const String& failingURL() const { return m_failingURL; } + const String& localizedDescription() const { return m_localizedDescription; } + + static bool compare(const SocketStreamError&, const SocketStreamError&); + + protected: + SocketStreamErrorBase() + : m_errorCode(0) + , m_isNull(true) + { + } + + explicit SocketStreamErrorBase(int errorCode) + : m_errorCode(errorCode) + , m_isNull(false) + { + } + + SocketStreamErrorBase(int errorCode, const String& failingURL, const String& localizedDescription) + : m_errorCode(errorCode) + , m_failingURL(failingURL) + , m_localizedDescription(localizedDescription) + , m_isNull(false) + { + } + + int m_errorCode; + String m_failingURL; + String m_localizedDescription; + bool m_isNull; + }; + + inline bool operator==(const SocketStreamError& a, const SocketStreamError& b) { return SocketStreamErrorBase::compare(a, b); } + inline bool operator!=(const SocketStreamError& a, const SocketStreamError& b) { return !(a == b); } + +} // namespace WebCore + +#endif // SocketStreamErrorBase_h diff --git a/Source/WebCore/platform/network/SocketStreamHandleBase.cpp b/Source/WebCore/platform/network/SocketStreamHandleBase.cpp new file mode 100644 index 0000000..8472713 --- /dev/null +++ b/Source/WebCore/platform/network/SocketStreamHandleBase.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "SocketStreamHandleBase.h" + +#include "SocketStreamHandle.h" +#include "SocketStreamHandleClient.h" + +namespace WebCore { + +const unsigned int bufferSize = 100 * 1024 * 1024; + +SocketStreamHandleBase::SocketStreamHandleBase(const KURL& url, SocketStreamHandleClient* client) + : m_url(url) + , m_client(client) + , m_state(Connecting) +{ +} + +SocketStreamHandleBase::SocketStreamState SocketStreamHandleBase::state() const +{ + return m_state; +} + +bool SocketStreamHandleBase::send(const char* data, int length) +{ + if (m_state == Connecting) + return false; + if (!m_buffer.isEmpty()) { + if (m_buffer.size() + length > bufferSize) { + // FIXME: report error to indicate that buffer has no more space. + return false; + } + m_buffer.append(data, length); + return true; + } + int bytesWritten = 0; + if (m_state == Open) + bytesWritten = platformSend(data, length); + if (bytesWritten < 0) + return false; + if (m_buffer.size() + length - bytesWritten > bufferSize) { + // FIXME: report error to indicate that buffer has no more space. + return false; + } + if (bytesWritten < length) + m_buffer.append(data + bytesWritten, length - bytesWritten); + return true; +} + +void SocketStreamHandleBase::close() +{ + RefPtr<SocketStreamHandle> protect(static_cast<SocketStreamHandle*>(this)); // platformClose calls the client, which may make the handle get deallocated immediately. + + platformClose(); + m_state = Closed; +} + +void SocketStreamHandleBase::setClient(SocketStreamHandleClient* client) +{ + ASSERT(!client || (!m_client && m_state == Connecting)); + m_client = client; +} + +bool SocketStreamHandleBase::sendPendingData() +{ + if (m_state != Open) + return false; + if (m_buffer.isEmpty()) + return false; + int bytesWritten = platformSend(m_buffer.data(), m_buffer.size()); + if (bytesWritten <= 0) + return false; + Vector<char> remainingData; + ASSERT(m_buffer.size() - bytesWritten <= bufferSize); + remainingData.append(m_buffer.data() + bytesWritten, m_buffer.size() - bytesWritten); + m_buffer.swap(remainingData); + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/SocketStreamHandleBase.h b/Source/WebCore/platform/network/SocketStreamHandleBase.h new file mode 100644 index 0000000..fc011dd --- /dev/null +++ b/Source/WebCore/platform/network/SocketStreamHandleBase.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamHandleBase_h +#define SocketStreamHandleBase_h + +#include "KURL.h" + +#include <wtf/Vector.h> + +namespace WebCore { + + class SocketStreamHandle; + class SocketStreamHandleClient; + + class SocketStreamHandleBase { + public: + enum SocketStreamState { Connecting, Open, Closed }; + virtual ~SocketStreamHandleBase() { } + SocketStreamState state() const; + + bool send(const char* data, int length); + void close(); + int bufferedAmount() const { return m_buffer.size(); } + + SocketStreamHandleClient* client() const { return m_client; } + void setClient(SocketStreamHandleClient*); + + protected: + SocketStreamHandleBase(const KURL&, SocketStreamHandleClient*); + + bool sendPendingData(); + virtual int platformSend(const char* data, int length) = 0; + virtual void platformClose() = 0; + + KURL m_url; + SocketStreamHandleClient* m_client; + Vector<char> m_buffer; + SocketStreamState m_state; + }; + +} // namespace WebCore + +#endif // SocketStreamHandleBase_h diff --git a/Source/WebCore/platform/network/SocketStreamHandleClient.h b/Source/WebCore/platform/network/SocketStreamHandleClient.h new file mode 100644 index 0000000..5d97ec0 --- /dev/null +++ b/Source/WebCore/platform/network/SocketStreamHandleClient.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamHandleClient_h +#define SocketStreamHandleClient_h + +namespace WebCore { + + class AuthenticationChallenge; + class KURL; + class SocketStreamError; + class SocketStreamHandle; + + class SocketStreamHandleClient { + public: + virtual ~SocketStreamHandleClient() { } + + virtual void didOpen(SocketStreamHandle*) { } + virtual void didClose(SocketStreamHandle*) { } + virtual void didReceiveData(SocketStreamHandle*, const char* /*data*/, int /*length*/) { } + + virtual void didFail(SocketStreamHandle*, const SocketStreamError&) { } + + // No authentication for streams per se, but proxy may ask for credentials. + virtual void didReceiveAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&) { } + virtual void didCancelAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&) { } + }; + +} // namespace WebCore + +#endif // SocketStreamHandleClient_h diff --git a/Source/WebCore/platform/network/android/AuthenticationChallenge.h b/Source/WebCore/platform/network/android/AuthenticationChallenge.h new file mode 100644 index 0000000..954bfd8 --- /dev/null +++ b/Source/WebCore/platform/network/android/AuthenticationChallenge.h @@ -0,0 +1,39 @@ +/* + * Copyright 2009, The Android Open Source Project + * Copyright (C) 2007 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 AuthenticationChallenge_h +#define AuthenticationChallenge_h + +#include "AuthenticationChallengeBase.h" + +namespace WebCore { + +class AuthenticationChallenge : public AuthenticationChallengeBase { +}; + +} + +#endif diff --git a/Source/WebCore/platform/network/android/CookieJarAndroid.cpp b/Source/WebCore/platform/network/android/CookieJarAndroid.cpp new file mode 100644 index 0000000..f3b343e --- /dev/null +++ b/Source/WebCore/platform/network/android/CookieJarAndroid.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2007, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "CookieJar.h" + +#include "PlatformBridge.h" + +namespace WebCore { + +void setCookies(Document* document, const KURL& url, const String& value) +{ + PlatformBridge::setCookies(document, url, value); +} + +String cookies(const Document* document, const KURL& url) +{ + return PlatformBridge::cookies(document, url); +} + +String cookieRequestHeaderFieldValue(const Document* document, const KURL& url) +{ + // FIXME: include HttpOnly cookie. + return PlatformBridge::cookies(document, url); +} + +bool cookiesEnabled(const Document* document) +{ + return PlatformBridge::cookiesEnabled(document); +} + +} diff --git a/Source/WebCore/platform/network/android/NetworkStateNotifierAndroid.cpp b/Source/WebCore/platform/network/android/NetworkStateNotifierAndroid.cpp new file mode 100644 index 0000000..7cc6ceb --- /dev/null +++ b/Source/WebCore/platform/network/android/NetworkStateNotifierAndroid.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "NetworkStateNotifier.h" + +namespace WebCore { + +void NetworkStateNotifier::networkStateChange(bool online) +{ + if (m_isOnLine == online) + return; + + m_isOnLine = online; + + if (m_networkStateChangedFunction) + m_networkStateChangedFunction(); +} + +// TODO: Upstream to webkit.org +void NetworkStateNotifier::networkTypeChange(Connection::ConnectionType type) +{ + if (m_type == type) + return; + + m_type = type; + + if (m_networkStateChangedFunction) + m_networkStateChangedFunction(); +} + +} diff --git a/Source/WebCore/platform/network/android/ProxyServerAndroid.cpp b/Source/WebCore/platform/network/android/ProxyServerAndroid.cpp new file mode 100644 index 0000000..2f813b5 --- /dev/null +++ b/Source/WebCore/platform/network/android/ProxyServerAndroid.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2010, The Android Open Source Project + * 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 THE COPYRIGHT HOLDERS ``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 "ProxyServer.h" + +#include "NotImplemented.h" + +namespace WebCore { + +Vector<ProxyServer> proxyServersForURL(const KURL&, const NetworkingContext*) +{ + notImplemented(); + return Vector<ProxyServer>(); +} + +} diff --git a/Source/WebCore/platform/network/android/ResourceError.h b/Source/WebCore/platform/network/android/ResourceError.h new file mode 100644 index 0000000..072c1ae --- /dev/null +++ b/Source/WebCore/platform/network/android/ResourceError.h @@ -0,0 +1,44 @@ +/* + * Copyright 2009, The Android Open Source Project + * Copyright (C) 2006 Apple Computer, 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 ResourceError_h +#define ResourceError_h + +#include "ResourceErrorBase.h" + +namespace WebCore { + +class ResourceError : public ResourceErrorBase { +public: + ResourceError() { } + + ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) + : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) { } +}; + +} + +#endif // ResourceError_h_ diff --git a/Source/WebCore/platform/network/android/ResourceHandleAndroid.cpp b/Source/WebCore/platform/network/android/ResourceHandleAndroid.cpp new file mode 100644 index 0000000..8837cf8 --- /dev/null +++ b/Source/WebCore/platform/network/android/ResourceHandleAndroid.cpp @@ -0,0 +1,163 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "ResourceHandle.h" + +#include "CachedResourceLoader.h" +#include "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "MainResourceLoader.h" +#include "NotImplemented.h" +#include "ResourceHandleClient.h" +#include "ResourceHandleInternal.h" +#include "ResourceLoaderAndroid.h" +#include "Settings.h" +#include <wtf/text/CString.h> + +namespace WebCore { + +ResourceHandleInternal::~ResourceHandleInternal() +{ +} + +ResourceHandle::~ResourceHandle() +{ +} + +bool ResourceHandle::start(NetworkingContext* context) +{ + MainResourceLoader* mainLoader = context->mainResourceLoader(); + bool isMainResource = static_cast<void*>(mainLoader) == static_cast<void*>(client()); + RefPtr<ResourceLoaderAndroid> loader = ResourceLoaderAndroid::start(this, d->m_firstRequest, context->frameLoaderClient(), isMainResource, false); + + if (loader) { + d->m_loader = loader.release(); + return true; + } + + return false; +} + +void ResourceHandle::cancel() +{ + if (d->m_loader) + d->m_loader->cancel(); +} + +PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() +{ + notImplemented(); + return 0; +} + +bool ResourceHandle::supportsBufferedData() +{ + // We don't support buffering data on the native side. + notImplemented(); + return false; +} + +#if PLATFORM(ANDROID) +// TODO: this needs upstreaming. +void ResourceHandle::pauseLoad(bool pause) +{ + if (d->m_loader) + d->m_loader->pauseLoad(pause); +} +#endif + +void ResourceHandle::platformSetDefersLoading(bool) +{ + notImplemented(); +} + +// This static method is called to check to see if a POST response is in +// the cache. The JNI call through to the HTTP cache stored on the Java +// side may be slow, but is only used during a navigation to +// a POST response. +bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*) +{ + // set the cache policy correctly, copied from + // network/mac/ResourceHandleMac.mm + request.setCachePolicy(ReturnCacheDataDontLoad); + FormData* formData = request.httpBody(); + return ResourceLoaderAndroid::willLoadFromCache(request.url(), formData ? formData->identifier() : 0); +} + +bool ResourceHandle::loadsBlocked() +{ + // FIXME, need to check whether connection pipe is blocked. + // return false for now + return false; +} + +// Class to handle synchronized loading of resources. +class SyncLoader : public ResourceHandleClient { +public: + SyncLoader(ResourceError& error, ResourceResponse& response, WTF::Vector<char>& data) + { + m_error = &error; + m_response = &response; + m_data = &data; + } + ~SyncLoader() {} + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse& response) + { + *m_response = response; + } + + virtual void didReceiveData(ResourceHandle*, const char* data, int len, int lengthReceived) + { + m_data->append(data, len); + } + + virtual void didFail(ResourceHandle*, const ResourceError& error) + { + *m_error = error; + } + +private: + ResourceError* m_error; + ResourceResponse* m_response; + WTF::Vector<char>* m_data; +}; + +void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, + StoredCredentials, ResourceError& error, ResourceResponse& response, WTF::Vector<char>& data) +{ + SyncLoader s(error, response, data); + RefPtr<ResourceHandle> h = adoptRef(new ResourceHandle(request, &s, false, false)); + // This blocks until the load is finished. + // Use the request owned by the ResourceHandle. This has had the username + // and password (if present) stripped from the URL in + // ResourceHandleInternal::ResourceHandleInternal(). This matches the + // behaviour in the asynchronous case. + ResourceLoaderAndroid::start(h.get(), request, context->frameLoaderClient(), false, true); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/android/ResourceLoaderAndroid.h b/Source/WebCore/platform/network/android/ResourceLoaderAndroid.h new file mode 100644 index 0000000..f627d62 --- /dev/null +++ b/Source/WebCore/platform/network/android/ResourceLoaderAndroid.h @@ -0,0 +1,56 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 ResourceLoaderAndroid_h +#define ResourceLoaderAndroid_h + +#include <ResourceRequest.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class FrameLoaderClient; +class ResourceHandle; + +class ResourceLoaderAndroid : public RefCounted<ResourceLoaderAndroid> { +public: + static PassRefPtr<ResourceLoaderAndroid> start(ResourceHandle*, const ResourceRequest&, FrameLoaderClient*, bool isMainResource, bool isSync); + virtual ~ResourceLoaderAndroid() { } + + virtual void cancel() = 0; + virtual void downloadFile() = 0; + // ANDROID TODO: This needs to be upstreamed. + virtual void pauseLoad(bool) = 0; + // END ANDROID TODO + + // Call to java to find out if this URL is in the cache + static bool willLoadFromCache(const WebCore::KURL&, int64_t identifier); +protected: + ResourceLoaderAndroid() { } +}; + +} +#endif // ResourceLoaderAndroid diff --git a/Source/WebCore/platform/network/android/ResourceRequest.h b/Source/WebCore/platform/network/android/ResourceRequest.h new file mode 100644 index 0000000..745c2ef --- /dev/null +++ b/Source/WebCore/platform/network/android/ResourceRequest.h @@ -0,0 +1,66 @@ +/* + * Copyright 2009, The Android Open Source Project + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 ResourceRequest_h +#define ResourceRequest_h + +#include "CachedResource.h" +#include "ResourceRequestBase.h" + +namespace WebCore { + +class ResourceRequest : public ResourceRequestBase { +public: + ResourceRequest(const String& url) + : ResourceRequestBase(KURL(ParsedURLString, url), UseProtocolCachePolicy) { } + + ResourceRequest(const KURL& url) : ResourceRequestBase(url, UseProtocolCachePolicy) { } + + ResourceRequest(const KURL& url, const String& referrer, ResourceRequestCachePolicy policy = UseProtocolCachePolicy) + : ResourceRequestBase(url, policy) + { + setHTTPReferrer(referrer); + } + + ResourceRequest() : ResourceRequestBase(KURL(), UseProtocolCachePolicy) { } + + void doUpdatePlatformRequest() { } + void doUpdateResourceRequest() { } + +private: + friend class ResourceRequestBase; + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } +}; + +struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { +}; + +} // namespace WebCore + +#endif // ResourceRequest_h diff --git a/Source/WebCore/platform/network/android/ResourceRequestAndroid.cpp b/Source/WebCore/platform/network/android/ResourceRequestAndroid.cpp new file mode 100644 index 0000000..00735f3 --- /dev/null +++ b/Source/WebCore/platform/network/android/ResourceRequestAndroid.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "ResourceRequest.h" + +namespace WebCore { + +unsigned initializeMaximumHTTPConnectionCountPerHost() +{ +#if USE(CHROME_NETWORK_STACK) + // The chromium network stack already handles limiting the number of + // parallel requests per host, so there's no need to do it here. Therefore, + // this is set to a high value that should never be hit in practice. + return 10000; +#else + // This is used by the loader to control the number of parallel load + // requests. Our java framework has 4 threads that can each pipeline up to + // 5 requests. Use 20 as a maximum number. + return 20; +#endif +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/android/ResourceResponse.h b/Source/WebCore/platform/network/android/ResourceResponse.h new file mode 100644 index 0000000..78307b9 --- /dev/null +++ b/Source/WebCore/platform/network/android/ResourceResponse.h @@ -0,0 +1,59 @@ +/* + * Copyright 2009, The Android Open Source Project + * Copyright (C) 2006 Apple Computer, 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 ResourceResponse_h +#define ResourceResponse_h + +#include "NotImplemented.h" +#include "ResourceResponseBase.h" + +namespace WebCore { + +class ResourceResponse : public ResourceResponseBase { +public: + ResourceResponse() : ResourceResponseBase() { } + + ResourceResponse(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) + : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename) { } + +private: + friend class ResourceResponseBase; + + void doUpdateResourceResponse() + { + notImplemented(); + } + + PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { } +}; + +struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { +}; + +} // namespace WebCore + +#endif // ResourceResponse_h diff --git a/Source/WebCore/platform/network/brew/DNSBrew.cpp b/Source/WebCore/platform/network/brew/DNSBrew.cpp new file mode 100644 index 0000000..ed1767a --- /dev/null +++ b/Source/WebCore/platform/network/brew/DNSBrew.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Company 100, 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 "DNS.h" + +#include "NotImplemented.h" + +namespace WebCore { + +void prefetchDNS(const String& hostname) +{ + // DNS prefetching is not implemented because the maximum number of UDP + // sockets is quite small in most BREW devices. + notImplemented(); +} + +} diff --git a/Source/WebCore/platform/network/brew/SocketStreamError.h b/Source/WebCore/platform/network/brew/SocketStreamError.h new file mode 100644 index 0000000..80dfa39 --- /dev/null +++ b/Source/WebCore/platform/network/brew/SocketStreamError.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamError_h +#define SocketStreamError_h + +#include "SocketStreamErrorBase.h" + +namespace WebCore { + +class SocketStreamError : public SocketStreamErrorBase { +public: + SocketStreamError() { } + explicit SocketStreamError(int errorCode) + : SocketStreamErrorBase(errorCode) + { + } + +}; + +} // namespace WebCore + +#endif // SocketStreamError_h diff --git a/Source/WebCore/platform/network/brew/SocketStreamHandle.h b/Source/WebCore/platform/network/brew/SocketStreamHandle.h new file mode 100644 index 0000000..d2d3e34 --- /dev/null +++ b/Source/WebCore/platform/network/brew/SocketStreamHandle.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamHandle_h +#define SocketStreamHandle_h + +#include "SocketStreamHandleBase.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class AuthenticationChallenge; +class Credential; +class SocketStreamHandleClient; +class SocketStreamHandlePrivate; + +class SocketStreamHandle : public RefCounted<SocketStreamHandle>, public SocketStreamHandleBase { +public: + static PassRefPtr<SocketStreamHandle> create(const KURL& url, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(url, client)); } + + virtual ~SocketStreamHandle(); + +protected: + virtual int platformSend(const char* data, int length); + virtual void platformClose(); + +private: + SocketStreamHandle(const KURL&, SocketStreamHandleClient*); + + // No authentication for streams per se, but proxy may ask for credentials. + void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); + void receivedCredential(const AuthenticationChallenge&, const Credential&); + void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&); + void receivedCancellation(const AuthenticationChallenge&); + + SocketStreamHandlePrivate* m_p; + friend class SocketStreamHandlePrivate; +}; + +} // namespace WebCore + +#endif // SocketStreamHandle_h diff --git a/Source/WebCore/platform/network/brew/SocketStreamHandleBrew.cpp b/Source/WebCore/platform/network/brew/SocketStreamHandleBrew.cpp new file mode 100644 index 0000000..5dc925e --- /dev/null +++ b/Source/WebCore/platform/network/brew/SocketStreamHandleBrew.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2010 Company 100, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "SocketStreamHandle.h" + +#include "KURL.h" +#include "Logging.h" +#include "NotImplemented.h" +#include "SocketStreamHandleClient.h" +#include "SocketStreamHandlePrivate.h" +#include <wtf/Vector.h> +#include <wtf/brew/ShellBrew.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +static void socketStreamConnectCallback(void* user, int nError) +{ + SocketStreamHandlePrivate* p = reinterpret_cast<SocketStreamHandlePrivate*>(user); + + if (nError != AEE_NET_SUCCESS) { + p->socketError(nError); + return; + } + + p->socketConnected(); +} + +static void getHostByNameCallback(void* user) +{ + SocketStreamHandlePrivate* p = reinterpret_cast<SocketStreamHandlePrivate*>(user); + + if (p->m_dns.nResult < 1 || p->m_dns.nResult > AEEDNSMAXADDRS) { + p->socketError(p->m_dns.nResult); + return; + } + + p->connect(); +} + +static void socketReadableCallback(void* user) +{ + SocketStreamHandlePrivate* p = reinterpret_cast<SocketStreamHandlePrivate*>(user); + p->socketReadyRead(); +} + +static INetMgr* networkManager() +{ + static INetMgr* s_netMgr; + + if (!s_netMgr) { + IShell* shell = reinterpret_cast<AEEApplet*>(GETAPPINSTANCE())->m_pIShell; + ISHELL_CreateInstance(shell, AEECLSID_NET, reinterpret_cast<void**>(&s_netMgr)); + ASSERT(s_netMgr); + } + + return s_netMgr; +} + +SocketStreamHandlePrivate::SocketStreamHandlePrivate(SocketStreamHandle* streamHandle, const KURL& url) +{ + m_streamHandle = streamHandle; + m_isSecure = url.protocolIs("wss"); + + m_socket.set(INETMGR_OpenSocket(networkManager(), AEE_SOCK_STREAM)); + if (!m_socket) + return; + + if (m_isSecure) + m_ssl = createInstance<ISSL>(AEECLSID_SSL); + + m_port = url.hasPort() ? url.port() : (m_isSecure ? 443 : 80); + + CALLBACK_Init(&m_dnsCallback, getHostByNameCallback, this); + m_dnsCallback.pfnCancel = 0; + + INETMGR_GetHostByName(networkManager(), &m_dns, url.host().latin1().data(), &m_dnsCallback); +} + +SocketStreamHandlePrivate::~SocketStreamHandlePrivate() +{ +} + +void SocketStreamHandlePrivate::socketConnected() +{ + if (m_streamHandle && m_streamHandle->client()) { + m_streamHandle->m_state = SocketStreamHandleBase::Open; + m_streamHandle->client()->didOpen(m_streamHandle); + } + + ISOCKET_Readable(m_socket.get(), socketReadableCallback, this); +} + +void SocketStreamHandlePrivate::socketReadyRead() +{ + if (m_streamHandle && m_streamHandle->client()) { + Vector<char> buffer(1024); + + int readSize = ISOCKET_Read(m_socket.get(), buffer.data(), buffer.size()); + if (readSize == AEE_NET_ERROR) { + socketError(ISOCKET_GetLastError(m_socket.get())); + return; + } + + m_streamHandle->client()->didReceiveData(m_streamHandle, buffer.data(), readSize); + } + + ISOCKET_Readable(m_socket.get(), socketReadableCallback, this); +} + +void SocketStreamHandlePrivate::connect() +{ + ISOCKET_Connect(m_socket.get(), m_dns.addrs[0], HTONS(m_port), socketStreamConnectCallback, this); +} + +int SocketStreamHandlePrivate::send(const char* data, int len) +{ + if (!m_socket) + return 0; + + int sentSize = ISOCKET_Write(m_socket.get(), reinterpret_cast<byte*>(const_cast<char*>(data)), len); + if (sentSize == AEE_NET_ERROR) { + socketError(ISOCKET_GetLastError(m_socket.get())); + return 0; + } + + return sentSize; +} + +void SocketStreamHandlePrivate::close() +{ + m_socket.clear(); +} + +void SocketStreamHandlePrivate::socketClosed() +{ + if (m_streamHandle && m_streamHandle->client()) { + SocketStreamHandle* streamHandle = m_streamHandle; + m_streamHandle = 0; + // This following call deletes _this_. Nothing should be after it. + streamHandle->client()->didClose(streamHandle); + } +} + +void SocketStreamHandlePrivate::socketError(int error) +{ + // FIXME - in the future, we might not want to treat all errors as fatal. + if (m_streamHandle && m_streamHandle->client()) { + SocketStreamHandle* streamHandle = m_streamHandle; + m_streamHandle = 0; + // This following call deletes _this_. Nothing should be after it. + streamHandle->client()->didClose(streamHandle); + } +} + +SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient* client) + : SocketStreamHandleBase(url, client) +{ + LOG(Network, "SocketStreamHandle %p new client %p", this, m_client); + m_p = new SocketStreamHandlePrivate(this, url); +} + +SocketStreamHandle::~SocketStreamHandle() +{ + LOG(Network, "SocketStreamHandle %p delete", this); + setClient(0); + delete m_p; +} + +int SocketStreamHandle::platformSend(const char* data, int len) +{ + LOG(Network, "SocketStreamHandle %p platformSend", this); + return m_p->send(data, len); +} + +void SocketStreamHandle::platformClose() +{ + LOG(Network, "SocketStreamHandle %p platformClose", this); + m_p->close(); +} + +void SocketStreamHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&) +{ + notImplemented(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/brew/SocketStreamHandlePrivate.h b/Source/WebCore/platform/network/brew/SocketStreamHandlePrivate.h new file mode 100644 index 0000000..3c6fd18 --- /dev/null +++ b/Source/WebCore/platform/network/brew/SocketStreamHandlePrivate.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Company 100, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamHandlePrivate_h +#define SocketStreamHandlePrivate_h + +#include "SocketStreamHandleBase.h" + +#include <AEENet.h> +#include <AEESSL.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class SocketStreamHandleClient; + +class SocketStreamHandlePrivate { +public: + SocketStreamHandlePrivate(SocketStreamHandle*, const KURL&); + ~SocketStreamHandlePrivate(); + + void socketConnected(); + void socketReadyRead(); + void socketClosed(); + void socketError(int); + + void connect(); + int send(const char* data, int len); + void close(); + + AEEDNSResult m_dns; + AEECallback m_dnsCallback; + unsigned int m_port; + bool m_isSecure; + + OwnPtr<ISocket> m_socket; + OwnPtr<ISSL> m_ssl; + SocketStreamHandle* m_streamHandle; +}; + +} + +#endif // SocketStreamHandlePrivate_h diff --git a/Source/WebCore/platform/network/cf/AuthenticationCF.cpp b/Source/WebCore/platform/network/cf/AuthenticationCF.cpp new file mode 100644 index 0000000..5168a48 --- /dev/null +++ b/Source/WebCore/platform/network/cf/AuthenticationCF.cpp @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 "AuthenticationCF.h" + +#if USE(CFNETWORK) + +#include "AuthenticationChallenge.h" +#include "AuthenticationClient.h" +#include "Credential.h" +#include "ProtectionSpace.h" + +#include <CFNetwork/CFURLAuthChallengePriv.h> +#include <CFNetwork/CFURLCredentialPriv.h> +#include <CFNetwork/CFURLProtectionSpacePriv.h> + +namespace WebCore { + +AuthenticationChallenge::AuthenticationChallenge(const ProtectionSpace& protectionSpace, + const Credential& proposedCredential, + unsigned previousFailureCount, + const ResourceResponse& response, + const ResourceError& error) + : AuthenticationChallengeBase(protectionSpace, + proposedCredential, + previousFailureCount, + response, + error) +{ +} + +AuthenticationChallenge::AuthenticationChallenge(CFURLAuthChallengeRef cfChallenge, + AuthenticationClient* authenticationClient) + : AuthenticationChallengeBase(core(CFURLAuthChallengeGetProtectionSpace(cfChallenge)), + core(CFURLAuthChallengeGetProposedCredential(cfChallenge)), + CFURLAuthChallengeGetPreviousFailureCount(cfChallenge), + (CFURLResponseRef)CFURLAuthChallengeGetFailureResponse(cfChallenge), + CFURLAuthChallengeGetError(cfChallenge)) + , m_authenticationClient(authenticationClient) + , m_cfChallenge(cfChallenge) +{ +} + +bool AuthenticationChallenge::platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b) +{ + if (a.authenticationClient() != b.authenticationClient()) + return false; + + if (a.cfURLAuthChallengeRef() != b.cfURLAuthChallengeRef()) + return false; + + return true; +} + +CFURLAuthChallengeRef createCF(const AuthenticationChallenge& coreChallenge) +{ + CFURLProtectionSpaceRef protectionSpace = createCF(coreChallenge.protectionSpace()); + CFURLCredentialRef credential = createCF(coreChallenge.proposedCredential()); + + CFURLAuthChallengeRef result = CFURLAuthChallengeCreate(0, protectionSpace, credential, + coreChallenge.previousFailureCount(), + coreChallenge.failureResponse().cfURLResponse(), + coreChallenge.error()); + CFRelease(protectionSpace); + CFRelease(credential); + return result; +} + +CFURLCredentialRef createCF(const Credential& coreCredential) +{ + CFURLCredentialPersistence persistence = kCFURLCredentialPersistenceNone; + switch (coreCredential.persistence()) { + case CredentialPersistenceNone: + break; + case CredentialPersistenceForSession: + persistence = kCFURLCredentialPersistenceForSession; + break; + case CredentialPersistencePermanent: + persistence = kCFURLCredentialPersistencePermanent; + break; + default: + ASSERT_NOT_REACHED(); + } + + CFStringRef user = coreCredential.user().createCFString(); + CFStringRef password = coreCredential.password().createCFString(); + CFURLCredentialRef result = CFURLCredentialCreate(0, user, password, 0, persistence); + CFRelease(user); + CFRelease(password); + + return result; +} + +CFURLProtectionSpaceRef createCF(const ProtectionSpace& coreSpace) +{ + CFURLProtectionSpaceServerType serverType = kCFURLProtectionSpaceServerHTTP; + switch (coreSpace.serverType()) { + case ProtectionSpaceServerHTTP: + serverType = kCFURLProtectionSpaceServerHTTP; + break; + case ProtectionSpaceServerHTTPS: + serverType = kCFURLProtectionSpaceServerHTTPS; + break; + case ProtectionSpaceServerFTP: + serverType = kCFURLProtectionSpaceServerFTP; + break; + case ProtectionSpaceServerFTPS: + serverType = kCFURLProtectionSpaceServerFTPS; + break; + case ProtectionSpaceProxyHTTP: + serverType = kCFURLProtectionSpaceProxyHTTP; + break; + case ProtectionSpaceProxyHTTPS: + serverType = kCFURLProtectionSpaceProxyHTTPS; + break; + case ProtectionSpaceProxyFTP: + serverType = kCFURLProtectionSpaceProxyFTP; + break; + case ProtectionSpaceProxySOCKS: + serverType = kCFURLProtectionSpaceProxySOCKS; + break; + default: + ASSERT_NOT_REACHED(); + } + + CFURLProtectionSpaceAuthenticationScheme scheme = kCFURLProtectionSpaceAuthenticationSchemeDefault; + switch (coreSpace.authenticationScheme()) { + case ProtectionSpaceAuthenticationSchemeDefault: + scheme = kCFURLProtectionSpaceAuthenticationSchemeDefault; + break; + case ProtectionSpaceAuthenticationSchemeHTTPBasic: + scheme = kCFURLProtectionSpaceAuthenticationSchemeHTTPBasic; + break; + case ProtectionSpaceAuthenticationSchemeHTTPDigest: + scheme = kCFURLProtectionSpaceAuthenticationSchemeHTTPDigest; + break; + case ProtectionSpaceAuthenticationSchemeHTMLForm: + scheme = kCFURLProtectionSpaceAuthenticationSchemeHTMLForm; + break; + case ProtectionSpaceAuthenticationSchemeNTLM: + scheme = kCFURLProtectionSpaceAuthenticationSchemeNTLM; + break; + case ProtectionSpaceAuthenticationSchemeNegotiate: + scheme = kCFURLProtectionSpaceAuthenticationSchemeNegotiate; + break; + default: + ASSERT_NOT_REACHED(); + } + + CFStringRef host = coreSpace.host().createCFString(); + CFStringRef realm = coreSpace.realm().createCFString(); + CFURLProtectionSpaceRef result = CFURLProtectionSpaceCreate(0, host, coreSpace.port(), serverType, realm, scheme); + CFRelease(host); + CFRelease(realm); + + return result; +} + +Credential core(CFURLCredentialRef cfCredential) +{ + if (!cfCredential) + return Credential(); + + CredentialPersistence persistence = CredentialPersistenceNone; + switch (CFURLCredentialGetPersistence(cfCredential)) { + case kCFURLCredentialPersistenceNone: + break; + case kCFURLCredentialPersistenceForSession: + persistence = CredentialPersistenceForSession; + break; + case kCFURLCredentialPersistencePermanent: + persistence = CredentialPersistencePermanent; + break; + default: + ASSERT_NOT_REACHED(); + } + + return Credential(CFURLCredentialGetUsername(cfCredential), CFURLCredentialCopyPassword(cfCredential), persistence); +} + +ProtectionSpace core(CFURLProtectionSpaceRef cfSpace) +{ + ProtectionSpaceServerType serverType = ProtectionSpaceServerHTTP; + + switch (CFURLProtectionSpaceGetServerType(cfSpace)) { + case kCFURLProtectionSpaceServerHTTP: + break; + case kCFURLProtectionSpaceServerHTTPS: + serverType = ProtectionSpaceServerHTTPS; + break; + case kCFURLProtectionSpaceServerFTP: + serverType = ProtectionSpaceServerFTP; + break; + case kCFURLProtectionSpaceServerFTPS: + serverType = ProtectionSpaceServerFTPS; + break; + case kCFURLProtectionSpaceProxyHTTP: + serverType = ProtectionSpaceProxyHTTP; + break; + case kCFURLProtectionSpaceProxyHTTPS: + serverType = ProtectionSpaceProxyHTTPS; + break; + case kCFURLProtectionSpaceProxyFTP: + serverType = ProtectionSpaceProxyFTP; + break; + case kCFURLProtectionSpaceProxySOCKS: + serverType = ProtectionSpaceProxySOCKS; + break; + default: + ASSERT_NOT_REACHED(); + } + + ProtectionSpaceAuthenticationScheme scheme = ProtectionSpaceAuthenticationSchemeDefault; + + switch (CFURLProtectionSpaceGetAuthenticationScheme(cfSpace)) { + case kCFURLProtectionSpaceAuthenticationSchemeDefault: + scheme = ProtectionSpaceAuthenticationSchemeDefault; + break; + case kCFURLProtectionSpaceAuthenticationSchemeHTTPBasic: + scheme = ProtectionSpaceAuthenticationSchemeHTTPBasic; + break; + case kCFURLProtectionSpaceAuthenticationSchemeHTTPDigest: + scheme = ProtectionSpaceAuthenticationSchemeHTTPDigest; + break; + case kCFURLProtectionSpaceAuthenticationSchemeHTMLForm: + scheme = ProtectionSpaceAuthenticationSchemeHTMLForm; + break; + case kCFURLProtectionSpaceAuthenticationSchemeNTLM: + scheme = ProtectionSpaceAuthenticationSchemeNTLM; + break; + case kCFURLProtectionSpaceAuthenticationSchemeNegotiate: + scheme = ProtectionSpaceAuthenticationSchemeNegotiate; + break; + default: + scheme = ProtectionSpaceAuthenticationSchemeUnknown; + ASSERT_NOT_REACHED(); + } + + return ProtectionSpace(CFURLProtectionSpaceGetHost(cfSpace), + CFURLProtectionSpaceGetPort(cfSpace), + serverType, + CFURLProtectionSpaceGetRealm(cfSpace), + scheme); +} + +}; + +#endif // USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/cf/AuthenticationCF.h b/Source/WebCore/platform/network/cf/AuthenticationCF.h new file mode 100644 index 0000000..0e34a56 --- /dev/null +++ b/Source/WebCore/platform/network/cf/AuthenticationCF.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 AuthenticationCF_h +#define AuthenticationCF_h + +#if USE(CFNETWORK) + +#include <CFNetwork/CFURLCredentialPriv.h> + +typedef struct _CFURLAuthChallenge* CFURLAuthChallengeRef; +typedef struct _CFURLProtectionSpace* CFURLProtectionSpaceRef; + +namespace WebCore { + +class AuthenticationChallenge; +class Credential; +class ProtectionSpace; + +CFURLAuthChallengeRef createCF(const AuthenticationChallenge&); +CFURLCredentialRef createCF(const Credential&); +CFURLProtectionSpaceRef createCF(const ProtectionSpace&); + +Credential core(CFURLCredentialRef); +ProtectionSpace core(CFURLProtectionSpaceRef); + +} + +#endif // USE(CFNETWORK) + +#endif // AuthenticationCF_h diff --git a/Source/WebCore/platform/network/cf/AuthenticationChallenge.h b/Source/WebCore/platform/network/cf/AuthenticationChallenge.h new file mode 100644 index 0000000..f396b54 --- /dev/null +++ b/Source/WebCore/platform/network/cf/AuthenticationChallenge.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 AuthenticationChallenge_h +#define AuthenticationChallenge_h + +#include "AuthenticationChallengeBase.h" +#include "AuthenticationClient.h" +#include <wtf/RefPtr.h> + +#if USE(CFNETWORK) +typedef struct _CFURLAuthChallenge* CFURLAuthChallengeRef; +#else +#ifndef __OBJC__ +typedef struct objc_object *id; +class NSURLAuthenticationChallenge; +#else +@class NSURLAuthenticationChallenge; +#endif +#endif + +namespace WebCore { + +class AuthenticationChallenge : public AuthenticationChallengeBase { +public: + AuthenticationChallenge() {} + AuthenticationChallenge(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error); +#if USE(CFNETWORK) + AuthenticationChallenge(CFURLAuthChallengeRef, AuthenticationClient*); + + AuthenticationClient* authenticationClient() const { return m_authenticationClient.get(); } + void setAuthenticationClient(AuthenticationClient* client) { m_authenticationClient = client; } + + CFURLAuthChallengeRef cfURLAuthChallengeRef() const { return m_cfChallenge.get(); } +#else + AuthenticationChallenge(NSURLAuthenticationChallenge *); + + id sender() const { return m_sender.get(); } + NSURLAuthenticationChallenge *nsURLAuthenticationChallenge() const { return m_nsChallenge.get(); } + + void setAuthenticationClient(AuthenticationClient*); // Changes sender to one that invokes client methods. + AuthenticationClient* authenticationClient() const; +#endif + +private: + friend class AuthenticationChallengeBase; + static bool platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b); + +#if USE(CFNETWORK) + RefPtr<AuthenticationClient> m_authenticationClient; + RetainPtr<CFURLAuthChallengeRef> m_cfChallenge; +#else + RetainPtr<id> m_sender; // Always the same as [m_macChallenge.get() sender], cached here for performance. + RetainPtr<NSURLAuthenticationChallenge *> m_nsChallenge; +#endif +}; + +} + +#endif diff --git a/Source/WebCore/platform/network/cf/CookieJarCFNet.cpp b/Source/WebCore/platform/network/cf/CookieJarCFNet.cpp new file mode 100644 index 0000000..659b719 --- /dev/null +++ b/Source/WebCore/platform/network/cf/CookieJarCFNet.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2006, 2007, 2008 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 COMPUTER, 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 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 "CookieJar.h" + +#if USE(CFNETWORK) + +#include "Cookie.h" +#include "CookieStorageCFNet.h" +#include "Document.h" +#include "KURL.h" +#include "PlatformString.h" +#include "ResourceHandle.h" +#include <CFNetwork/CFHTTPCookiesPriv.h> +#include <CoreFoundation/CoreFoundation.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <windows.h> + +namespace WebCore { + +static const CFStringRef s_setCookieKeyCF = CFSTR("Set-Cookie"); +static const CFStringRef s_cookieCF = CFSTR("Cookie"); + +static RetainPtr<CFArrayRef> filterCookies(CFArrayRef unfilteredCookies) +{ + CFIndex count = CFArrayGetCount(unfilteredCookies); + RetainPtr<CFMutableArrayRef> filteredCookies(AdoptCF, CFArrayCreateMutable(0, count, &kCFTypeArrayCallBacks)); + for (CFIndex i = 0; i < count; ++i) { + CFHTTPCookieRef cookie = (CFHTTPCookieRef)CFArrayGetValueAtIndex(unfilteredCookies, i); + + // <rdar://problem/5632883> CFHTTPCookieStorage would store an empty cookie, + // which would be sent as "Cookie: =". We have a workaround in setCookies() to prevent + // that, but we also need to avoid sending cookies that were previously stored, and + // there's no harm to doing this check because such a cookie is never valid. + if (!CFStringGetLength(CFHTTPCookieGetName(cookie))) + continue; + + if (CFHTTPCookieIsHTTPOnly(cookie)) + continue; + + CFArrayAppendValue(filteredCookies.get(), cookie); + } + return filteredCookies; +} + +void setCookies(Document* document, const KURL& url, const String& value) +{ + // <rdar://problem/5632883> CFHTTPCookieStorage stores an empty cookie, which would be sent as "Cookie: =". + if (value.isEmpty()) + return; + + CFHTTPCookieStorageRef cookieStorage = currentCookieStorage(); + if (!cookieStorage) + return; + + RetainPtr<CFURLRef> urlCF(AdoptCF, url.createCFURL()); + RetainPtr<CFURLRef> firstPartyForCookiesCF(AdoptCF, document->firstPartyForCookies().createCFURL()); + + // <http://bugs.webkit.org/show_bug.cgi?id=6531>, <rdar://4409034> + // cookiesWithResponseHeaderFields doesn't parse cookies without a value + String cookieString = value.contains('=') ? value : value + "="; + + RetainPtr<CFStringRef> cookieStringCF(AdoptCF, cookieString.createCFString()); + RetainPtr<CFDictionaryRef> headerFieldsCF(AdoptCF, CFDictionaryCreate(kCFAllocatorDefault, + (const void**)&s_setCookieKeyCF, (const void**)&cookieStringCF, 1, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + + RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieCreateWithResponseHeaderFields(kCFAllocatorDefault, + headerFieldsCF.get(), urlCF.get())); + + CFHTTPCookieStorageSetCookies(cookieStorage, filterCookies(cookiesCF.get()).get(), urlCF.get(), firstPartyForCookiesCF.get()); +} + +String cookies(const Document* /*document*/, const KURL& url) +{ + CFHTTPCookieStorageRef cookieStorage = currentCookieStorage(); + if (!cookieStorage) + return String(); + + RetainPtr<CFURLRef> urlCF(AdoptCF, url.createCFURL()); + + bool secure = url.protocolIs("https"); + RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieStorageCopyCookiesForURL(cookieStorage, urlCF.get(), secure)); + RetainPtr<CFDictionaryRef> headerCF(AdoptCF, CFHTTPCookieCopyRequestHeaderFields(kCFAllocatorDefault, filterCookies(cookiesCF.get()).get())); + return (CFStringRef)CFDictionaryGetValue(headerCF.get(), s_cookieCF); +} + +String cookieRequestHeaderFieldValue(const Document* /*document*/, const KURL& url) +{ + CFHTTPCookieStorageRef cookieStorage = currentCookieStorage(); + if (!cookieStorage) + return String(); + + RetainPtr<CFURLRef> urlCF(AdoptCF, url.createCFURL()); + + bool secure = url.protocolIs("https"); + RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieStorageCopyCookiesForURL(cookieStorage, urlCF.get(), secure)); + RetainPtr<CFDictionaryRef> headerCF(AdoptCF, CFHTTPCookieCopyRequestHeaderFields(kCFAllocatorDefault, cookiesCF.get())); + return (CFStringRef)CFDictionaryGetValue(headerCF.get(), s_cookieCF); +} + +bool cookiesEnabled(const Document* /*document*/) +{ + CFHTTPCookieStorageAcceptPolicy policy = CFHTTPCookieStorageAcceptPolicyOnlyFromMainDocumentDomain; + if (CFHTTPCookieStorageRef cookieStorage = currentCookieStorage()) + policy = CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage); + return policy == CFHTTPCookieStorageAcceptPolicyOnlyFromMainDocumentDomain || policy == CFHTTPCookieStorageAcceptPolicyAlways; +} + +bool getRawCookies(const Document*, const KURL& url, Vector<Cookie>& rawCookies) +{ + rawCookies.clear(); + CFHTTPCookieStorageRef cookieStorage = currentCookieStorage(); + if (!cookieStorage) + return false; + + RetainPtr<CFURLRef> urlCF(AdoptCF, url.createCFURL()); + + bool sendSecureCookies = url.protocolIs("https"); + RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieStorageCopyCookiesForURL(cookieStorage, urlCF.get(), sendSecureCookies)); + + CFIndex count = CFArrayGetCount(cookiesCF.get()); + rawCookies.reserveCapacity(count); + + for (CFIndex i = 0; i < count; i++) { + CFHTTPCookieRef cookie = (CFHTTPCookieRef)CFArrayGetValueAtIndex(cookiesCF.get(), i); + String name = CFHTTPCookieGetName(cookie); + String value = CFHTTPCookieGetValue(cookie); + String domain = CFHTTPCookieGetDomain(cookie); + String path = CFHTTPCookieGetPath(cookie); + + double expires = (CFDateGetAbsoluteTime(CFHTTPCookieGetExpiratonDate(cookie)) + kCFAbsoluteTimeIntervalSince1970) * 1000; + + bool httpOnly = CFHTTPCookieIsHTTPOnly(cookie); + bool secure = CFHTTPCookieIsSecure(cookie); + bool session = false; // FIXME: Need API for if a cookie is a session cookie. + + rawCookies.uncheckedAppend(Cookie(name, value, domain, path, expires, httpOnly, secure, session)); + } + + return true; +} + +void deleteCookie(const Document*, const KURL& url, const String& name) +{ + CFHTTPCookieStorageRef cookieStorage = currentCookieStorage(); + if (!cookieStorage) + return; + + RetainPtr<CFURLRef> urlCF(AdoptCF, url.createCFURL()); + + bool sendSecureCookies = url.protocolIs("https"); + RetainPtr<CFArrayRef> cookiesCF(AdoptCF, CFHTTPCookieStorageCopyCookiesForURL(cookieStorage, urlCF.get(), sendSecureCookies)); + + CFIndex count = CFArrayGetCount(cookiesCF.get()); + for (CFIndex i = 0; i < count; i++) { + CFHTTPCookieRef cookie = (CFHTTPCookieRef)CFArrayGetValueAtIndex(cookiesCF.get(), i); + String cookieName = CFHTTPCookieGetName(cookie); + if (cookieName == name) { + CFHTTPCookieStorageDeleteCookie(cookieStorage, cookie); + break; + } + } +} + +} // namespace WebCore + +#endif // USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/cf/CookieStorageCFNet.cpp b/Source/WebCore/platform/network/cf/CookieStorageCFNet.cpp new file mode 100644 index 0000000..3deb688 --- /dev/null +++ b/Source/WebCore/platform/network/cf/CookieStorageCFNet.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2008 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 COMPUTER, 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 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 "CookieStorageCFNet.h" + +#if USE(CFNETWORK) + +#include <CFNetwork/CFHTTPCookiesPriv.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <wtf/MainThread.h> +#include <wtf/RetainPtr.h> + +namespace WebCore { + +static RetainPtr<CFHTTPCookieStorageRef> s_cookieStorage; + +CFHTTPCookieStorageRef currentCookieStorage() +{ + ASSERT(isMainThread()); + + if (s_cookieStorage) + return s_cookieStorage.get(); + return wkGetDefaultHTTPCookieStorage(); +} + +void setCurrentCookieStorage(CFHTTPCookieStorageRef cookieStorage) +{ + ASSERT(isMainThread()); + + s_cookieStorage = cookieStorage; +} + +void setCookieStoragePrivateBrowsingEnabled(bool enabled) +{ + ASSERT(isMainThread()); + + if (enabled) + s_cookieStorage.adoptCF(wkCreatePrivateHTTPCookieStorage()); + else + s_cookieStorage = 0; +} + +} // namespace WebCore + +#endif // USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/cf/CookieStorageCFNet.h b/Source/WebCore/platform/network/cf/CookieStorageCFNet.h new file mode 100644 index 0000000..0167587 --- /dev/null +++ b/Source/WebCore/platform/network/cf/CookieStorageCFNet.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 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 COMPUTER, 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 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 CookieStorageCFNet_h +#define CookieStorageCFNet_h + +#if USE(CFNETWORK) + +typedef struct OpaqueCFHTTPCookieStorage* CFHTTPCookieStorageRef; + +namespace WebCore { + + CFHTTPCookieStorageRef currentCookieStorage(); + + // Needed for WebKit1 API only. + void setCurrentCookieStorage(CFHTTPCookieStorageRef cookieStorage); + +} + +#endif // USE(CFNETWORK) + +#endif // CookieStorageCFNet_h diff --git a/Source/WebCore/platform/network/cf/CredentialStorageCFNet.cpp b/Source/WebCore/platform/network/cf/CredentialStorageCFNet.cpp new file mode 100644 index 0000000..b6c9973 --- /dev/null +++ b/Source/WebCore/platform/network/cf/CredentialStorageCFNet.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 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 "CredentialStorage.h" + +#if USE(CFNETWORK) + +#include "AuthenticationCF.h" +#include "Credential.h" +#include "ProtectionSpace.h" +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <wtf/RetainPtr.h> + +namespace WebCore { + +Credential CredentialStorage::getFromPersistentStorage(const ProtectionSpace& protectionSpace) +{ + RetainPtr<CFURLProtectionSpaceRef> protectionSpaceCF(AdoptCF, createCF(protectionSpace)); + RetainPtr<CFURLCredentialRef> credentialCF(AdoptCF, wkCopyCredentialFromCFPersistentStorage(protectionSpaceCF.get())); + return core(credentialCF.get()); +} + +} // namespace WebCore + +#endif // USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/cf/DNSCFNet.cpp b/Source/WebCore/platform/network/cf/DNSCFNet.cpp new file mode 100644 index 0000000..166abbf --- /dev/null +++ b/Source/WebCore/platform/network/cf/DNSCFNet.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2008 Collin Jackson <collinj@webkit.org> + * Copyright (C) 2009 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 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 "DNS.h" + +#include "KURL.h" +#include "Timer.h" +#include <wtf/HashSet.h> +#include <wtf/RetainPtr.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringHash.h> + +#if PLATFORM(WIN) +#include "LoaderRunLoopCF.h" +#endif + +#if defined(BUILDING_ON_LEOPARD) +#include <SystemConfiguration/SystemConfiguration.h> +#endif + +#ifdef BUILDING_ON_TIGER +// This function is available on Tiger, but not declared in the CFRunLoop.h header on Tiger. +extern "C" CFRunLoopRef CFRunLoopGetMain(); +#endif + +namespace WebCore { + +// When resolve queue is empty, we fire async resolution requests immediately (which is important if the prefetch is triggered by hovering). +// But during page parsing, we should coalesce identical requests to avoid stressing out CFHost. +const int namesToResolveImmediately = 4; + +// Coalesce prefetch requests for this long before sending them out. +const double coalesceDelayInSeconds = 1.0; + +// Sending many DNS requests at once can overwhelm some gateways. CFHost doesn't currently throttle for us, see <rdar://8105550>. +const int maxSimultaneousRequests = 8; + +// For a page has links to many outside sites, it is likely that the system DNS resolver won't be able to cache them all anyway, and we don't want +// to negatively affect other applications' performance by pushing their cached entries out. +// If we end up with lots of names to prefetch, some will be dropped. +const int maxRequestsToQueue = 64; + +// If there were queued names that couldn't be sent simultaneously, check the state of resolvers after this delay. +const double retryResolvingInSeconds = 0.1; + +static bool proxyIsEnabledInSystemPreferences() +{ + // Don't do DNS prefetch if proxies are involved. For many proxy types, the user agent is never exposed + // to the IP address during normal operation. Querying an internal DNS server may not help performance, + // as it doesn't necessarily look up the actual external IP. Also, if DNS returns a fake internal address, + // local caches may keep it even after re-connecting to another network. + +#if !defined(BUILDING_ON_LEOPARD) + RetainPtr<CFDictionaryRef> proxySettings(AdoptCF, CFNetworkCopySystemProxySettings()); +#else + RetainPtr<CFDictionaryRef> proxySettings(AdoptCF, SCDynamicStoreCopyProxies(0)); +#endif + if (!proxySettings) + return false; + + static CFURLRef httpCFURL = KURL(ParsedURLString, "http://example.com/").createCFURL(); + static CFURLRef httpsCFURL = KURL(ParsedURLString, "https://example.com/").createCFURL(); + + RetainPtr<CFArrayRef> httpProxyArray(AdoptCF, CFNetworkCopyProxiesForURL(httpCFURL, proxySettings.get())); + RetainPtr<CFArrayRef> httpsProxyArray(AdoptCF, CFNetworkCopyProxiesForURL(httpsCFURL, proxySettings.get())); + + CFIndex httpProxyCount = CFArrayGetCount(httpProxyArray.get()); + CFIndex httpsProxyCount = CFArrayGetCount(httpsProxyArray.get()); + if (httpProxyCount == 1 && CFEqual(CFDictionaryGetValue(static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(httpProxyArray.get(), 0)), kCFProxyTypeKey), kCFProxyTypeNone)) + httpProxyCount = 0; + if (httpsProxyCount == 1 && CFEqual(CFDictionaryGetValue(static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(httpsProxyArray.get(), 0)), kCFProxyTypeKey), kCFProxyTypeNone)) + httpsProxyCount = 0; + + return httpProxyCount || httpsProxyCount; +} + +class DNSResolveQueue : public TimerBase { +public: + static DNSResolveQueue& shared(); + void add(const String&); + void decrementRequestCount(); + +private: + DNSResolveQueue(); + + void resolve(const String&); + virtual void fired(); + HashSet<String> m_names; + int m_requestsInFlight; +}; + +DNSResolveQueue::DNSResolveQueue() + : m_requestsInFlight(0) +{ +} + +DNSResolveQueue& DNSResolveQueue::shared() +{ + DEFINE_STATIC_LOCAL(DNSResolveQueue, names, ()); + return names; +} + +void DNSResolveQueue::add(const String& name) +{ + // If there are no names queued, and few enough are in flight, resolve immediately (the mouse may be over a link). + if (!m_names.size()) { + if (proxyIsEnabledInSystemPreferences()) + return; + + if (atomicIncrement(&m_requestsInFlight) <= namesToResolveImmediately) { + resolve(name); + return; + } + atomicDecrement(&m_requestsInFlight); + } + + // It's better to not prefetch some names than to clog the queue. + // Dropping the newest names, because on a single page, these are likely to be below oldest ones. + if (m_names.size() < maxRequestsToQueue) { + m_names.add(name); + if (!isActive()) + startOneShot(coalesceDelayInSeconds); + } +} + +void DNSResolveQueue::decrementRequestCount() +{ + atomicDecrement(&m_requestsInFlight); +} + +void DNSResolveQueue::fired() +{ + if (proxyIsEnabledInSystemPreferences()) { + m_names.clear(); + return; + } + + int requestsAllowed = maxSimultaneousRequests - m_requestsInFlight; + + for (; !m_names.isEmpty() && requestsAllowed > 0; --requestsAllowed) { + atomicIncrement(&m_requestsInFlight); + HashSet<String>::iterator currentName = m_names.begin(); + resolve(*currentName); + m_names.remove(currentName); + } + + if (!m_names.isEmpty()) + startOneShot(retryResolvingInSeconds); +} + +static void clientCallback(CFHostRef theHost, CFHostInfoType, const CFStreamError*, void*) +{ + DNSResolveQueue::shared().decrementRequestCount(); // It's ok to call shared() from a secondary thread, the static variable has already been initialized by now. + CFRelease(theHost); +} + +void DNSResolveQueue::resolve(const String& hostname) +{ + ASSERT(isMainThread()); + + RetainPtr<CFStringRef> hostnameCF(AdoptCF, hostname.createCFString()); + RetainPtr<CFHostRef> host(AdoptCF, CFHostCreateWithName(0, hostnameCF.get())); + if (!host) { + atomicDecrement(&m_requestsInFlight); + return; + } + CFHostClientContext context = { 0, 0, 0, 0, 0 }; + Boolean result = CFHostSetClient(host.get(), clientCallback, &context); + ASSERT_UNUSED(result, result); +#if !PLATFORM(WIN) + CFHostScheduleWithRunLoop(host.get(), CFRunLoopGetMain(), kCFRunLoopCommonModes); +#else + // On Windows, we run a separate thread with CFRunLoop, which is where clientCallback will be called. + CFHostScheduleWithRunLoop(host.get(), loaderRunLoop(), kCFRunLoopDefaultMode); +#endif + CFHostStartInfoResolution(host.get(), kCFHostAddresses, 0); + host.releaseRef(); // The host will be released from clientCallback(). +} + +void prefetchDNS(const String& hostname) +{ + ASSERT(isMainThread()); + if (hostname.isEmpty()) + return; + DNSResolveQueue::shared().add(hostname); +} + +} diff --git a/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp b/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp new file mode 100644 index 0000000..eb0ec3a --- /dev/null +++ b/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2005, 2006, 2007 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 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. + */ + +/* originally written by Becky Willrich, additional code by Darin Adler */ + +#include "config.h" +#include "FormDataStreamCFNet.h" + +#if USE(CFNETWORK) + +#include "FileSystem.h" +#include "FormData.h" +#include <CFNetwork/CFURLRequestPriv.h> +#include <CoreFoundation/CFStreamAbstract.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <sys/types.h> +#include <wtf/Assertions.h> +#include <wtf/HashMap.h> +#include <wtf/RetainPtr.h> +#include <wtf/text/CString.h> + +#define USE_V1_CFSTREAM_CALLBACKS +#ifdef USE_V1_CFSTREAM_CALLBACKS +typedef CFReadStreamCallBacksV1 WCReadStreamCallBacks; +#else +typedef CFReadStreamCallBacks WCReadStreamCallBacks; +#endif + +namespace WebCore { + +static HashMap<CFReadStreamRef, RefPtr<FormData> >& getStreamFormDatas() +{ + static HashMap<CFReadStreamRef, RefPtr<FormData> > streamFormDatas; + return streamFormDatas; +} + +static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context); + +struct FormStreamFields { + CFMutableSetRef scheduledRunLoopPairs; + Vector<FormDataElement> remainingElements; // in reverse order + CFReadStreamRef currentStream; + char* currentData; + CFReadStreamRef formStream; +}; + +struct SchedulePair { + CFRunLoopRef runLoop; + CFStringRef mode; +}; + +static const void* pairRetain(CFAllocatorRef alloc, const void* value) +{ + const SchedulePair* pair = static_cast<const SchedulePair*>(value); + + SchedulePair* result = new SchedulePair; + CFRetain(pair->runLoop); + result->runLoop = pair->runLoop; + result->mode = CFStringCreateCopy(alloc, pair->mode); + return result; +} + +static void pairRelease(CFAllocatorRef alloc, const void* value) +{ + const SchedulePair* pair = static_cast<const SchedulePair*>(value); + + CFRelease(pair->runLoop); + CFRelease(pair->mode); + delete pair; +} + +static Boolean pairEqual(const void* a, const void* b) +{ + const SchedulePair* pairA = static_cast<const SchedulePair*>(a); + const SchedulePair* pairB = static_cast<const SchedulePair*>(b); + + return pairA->runLoop == pairB->runLoop && CFEqual(pairA->mode, pairB->mode); +} + +static CFHashCode pairHash(const void* value) +{ + const SchedulePair* pair = static_cast<const SchedulePair*>(value); + + return (CFHashCode)pair->runLoop ^ CFHash(pair->mode); +} + +static void closeCurrentStream(FormStreamFields *form) +{ + if (form->currentStream) { + CFReadStreamClose(form->currentStream); + CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL); + CFRelease(form->currentStream); + form->currentStream = NULL; + } + if (form->currentData) { + fastFree(form->currentData); + form->currentData = 0; + } +} + +static void scheduleWithPair(const void* value, void* context) +{ + const SchedulePair* pair = static_cast<const SchedulePair*>(value); + CFReadStreamRef stream = (CFReadStreamRef)context; + + CFReadStreamScheduleWithRunLoop(stream, pair->runLoop, pair->mode); +} + +static void advanceCurrentStream(FormStreamFields *form) +{ + closeCurrentStream(form); + + if (form->remainingElements.isEmpty()) + return; + + // Create the new stream. + FormDataElement& nextInput = form->remainingElements.last(); + if (nextInput.m_type == FormDataElement::data) { + size_t size = nextInput.m_data.size(); + char* data = nextInput.m_data.releaseBuffer(); + form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull); + form->currentData = data; + } else { + CFStringRef filename = nextInput.m_filename.createCFString(); +#if PLATFORM(WIN) + CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLWindowsPathStyle, FALSE); +#else + CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLPOSIXPathStyle, FALSE); +#endif + CFRelease(filename); + form->currentStream = CFReadStreamCreateWithFile(0, fileURL); + CFRelease(fileURL); + } + form->remainingElements.removeLast(); + + // Set up the callback. + CFStreamClientContext context = { 0, form, NULL, NULL, NULL }; + CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, + formEventCallback, &context); + + // Schedule with the current set of run loops. + CFSetApplyFunction(form->scheduledRunLoopPairs, scheduleWithPair, form->currentStream); +} + +static void openNextStream(FormStreamFields* form) +{ + // Skip over any streams we can't open. + // For some purposes we might want to return an error, but the current CFURLConnection + // can't really do anything useful with an error at this point, so this is better. + advanceCurrentStream(form); + while (form->currentStream && !CFReadStreamOpen(form->currentStream)) + advanceCurrentStream(form); +} + +static void* formCreate(CFReadStreamRef stream, void* context) +{ + FormData* formData = static_cast<FormData*>(context); + + CFSetCallBacks runLoopAndModeCallBacks = { 0, pairRetain, pairRelease, NULL, pairEqual, pairHash }; + + FormStreamFields* newInfo = new FormStreamFields; + newInfo->scheduledRunLoopPairs = CFSetCreateMutable(0, 0, &runLoopAndModeCallBacks); + newInfo->currentStream = NULL; + newInfo->currentData = 0; + newInfo->formStream = stream; // Don't retain. That would create a reference cycle. + + // Append in reverse order since we remove elements from the end. + size_t size = formData->elements().size(); + newInfo->remainingElements.reserveCapacity(size); + for (size_t i = 0; i < size; ++i) + newInfo->remainingElements.append(formData->elements()[size - i - 1]); + + getStreamFormDatas().set(stream, adoptRef(formData)); + + return newInfo; +} + +static void formFinalize(CFReadStreamRef stream, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + getStreamFormDatas().remove(stream); + + closeCurrentStream(form); + CFRelease(form->scheduledRunLoopPairs); + delete form; +} + +static Boolean formOpen(CFReadStreamRef stream, CFStreamError* error, Boolean* openComplete, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + openNextStream(form); + + *openComplete = TRUE; + error->error = 0; + return TRUE; +} + +static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + while (form->currentStream) { + CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength); + if (bytesRead < 0) { + *error = CFReadStreamGetError(form->currentStream); + return -1; + } + if (bytesRead > 0) { + error->error = 0; + *atEOF = FALSE; + return bytesRead; + } + openNextStream(form); + } + + error->error = 0; + *atEOF = TRUE; + return 0; +} + +static Boolean formCanRead(CFReadStreamRef stream, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) { + openNextStream(form); + } + if (!form->currentStream) { + CFReadStreamSignalEvent(stream, kCFStreamEventEndEncountered, 0); + return FALSE; + } + return CFReadStreamHasBytesAvailable(form->currentStream); +} + +static void formClose(CFReadStreamRef stream, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + closeCurrentStream(form); +} + +static void formSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + if (form->currentStream) + CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode); + SchedulePair pair = { runLoop, runLoopMode }; + CFSetAddValue(form->scheduledRunLoopPairs, &pair); +} + +static void formUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + if (form->currentStream) + CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode); + SchedulePair pair = { runLoop, runLoopMode }; + CFSetRemoveValue(form->scheduledRunLoopPairs, &pair); +} + +static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + switch (type) { + case kCFStreamEventHasBytesAvailable: + CFReadStreamSignalEvent(form->formStream, kCFStreamEventHasBytesAvailable, 0); + break; + case kCFStreamEventErrorOccurred: { + CFStreamError readStreamError = CFReadStreamGetError(stream); + CFReadStreamSignalEvent(form->formStream, kCFStreamEventErrorOccurred, &readStreamError); + break; + } + case kCFStreamEventEndEncountered: + openNextStream(form); + if (!form->currentStream) + CFReadStreamSignalEvent(form->formStream, kCFStreamEventEndEncountered, 0); + break; + case kCFStreamEventNone: + LOG_ERROR("unexpected kCFStreamEventNone"); + break; + case kCFStreamEventOpenCompleted: + LOG_ERROR("unexpected kCFStreamEventOpenCompleted"); + break; + case kCFStreamEventCanAcceptBytes: + LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes"); + break; + } +} + +void setHTTPBody(CFMutableURLRequestRef request, PassRefPtr<FormData> formData) +{ + if (!formData) { + wkCFURLRequestSetHTTPRequestBodyParts(request, 0); + return; + } + + size_t count = formData->elements().size(); + + if (count == 0) + return; + + // Handle the common special case of one piece of form data, with no files. + if (count == 1) { + const FormDataElement& element = formData->elements()[0]; + if (element.m_type == FormDataElement::data) { + CFDataRef data = CFDataCreate(0, reinterpret_cast<const UInt8 *>(element.m_data.data()), element.m_data.size()); + CFURLRequestSetHTTPRequestBody(request, data); + CFRelease(data); + return; + } + } + + RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks)); + + for (size_t i = 0; i < count; ++i) { + const FormDataElement& element = formData->elements()[i]; + if (element.m_type == FormDataElement::data) { + RetainPtr<CFDataRef> data(AdoptCF, CFDataCreate(0, reinterpret_cast<const UInt8*>(element.m_data.data()), element.m_data.size())); + CFArrayAppendValue(array.get(), data.get()); + } else { + RetainPtr<CFStringRef> filename(AdoptCF, element.m_filename.createCFString()); + CFArrayAppendValue(array.get(), filename.get()); + } + } + + wkCFURLRequestSetHTTPRequestBodyParts(request, array.get()); +} + +PassRefPtr<FormData> httpBodyFromRequest(CFURLRequestRef request) +{ + if (RetainPtr<CFDataRef> bodyData = CFURLRequestCopyHTTPRequestBody(request)) + return FormData::create(CFDataGetBytePtr(bodyData.get()), CFDataGetLength(bodyData.get())); + + if (RetainPtr<CFArrayRef> bodyParts = wkCFURLRequestCopyHTTPRequestBodyParts(request)) { + RefPtr<FormData> formData = FormData::create(); + + CFIndex count = CFArrayGetCount(bodyParts.get()); + for (CFIndex i = 0; i < count; i++) { + CFTypeRef bodyPart = CFArrayGetValueAtIndex(bodyParts.get(), i); + CFTypeID typeID = CFGetTypeID(bodyPart); + if (typeID == CFStringGetTypeID()) { + String filename = (CFStringRef)bodyPart; + formData->appendFile(filename); + } else if (typeID == CFDataGetTypeID()) { + CFDataRef data = (CFDataRef)bodyPart; + formData->appendData(CFDataGetBytePtr(data), CFDataGetLength(data)); + } else + ASSERT_NOT_REACHED(); + } + return formData.release(); + } + + // FIXME: what to do about arbitrary body streams? + return 0; +} + +} // namespace WebCore + +#endif // USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/cf/FormDataStreamCFNet.h b/Source/WebCore/platform/network/cf/FormDataStreamCFNet.h new file mode 100644 index 0000000..7260032 --- /dev/null +++ b/Source/WebCore/platform/network/cf/FormDataStreamCFNet.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005, 2006, 2007 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 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 FormDataStreamCFNet_h +#define FormDataStreamCFNet_h + +#if USE(CFNETWORK) + +#include <CoreFoundation/CoreFoundation.h> +#include <wtf/Forward.h> + +typedef struct _CFURLRequest* CFMutableURLRequestRef; +typedef const struct _CFURLRequest* CFURLRequestRef; + +namespace WebCore { + class FormData; + void setHTTPBody(CFMutableURLRequestRef, PassRefPtr<FormData>); + PassRefPtr<FormData> httpBodyFromRequest(CFURLRequestRef); +} + +#endif // USE(CFNETWORK) + +#endif // FormDataStreamCFNet_h diff --git a/Source/WebCore/platform/network/cf/LoaderRunLoopCF.cpp b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.cpp new file mode 100644 index 0000000..249fe43 --- /dev/null +++ b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2009 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 "LoaderRunLoopCF.h" + +#if USE(CFNETWORK) + +#include <wtf/Threading.h> + +namespace WebCore { + +static CFRunLoopRef loaderRunLoopObject = 0; + +static void emptyPerform(void*) +{ +} + +static void* runLoaderThread(void*) +{ + loaderRunLoopObject = CFRunLoopGetCurrent(); + + // Must add a source to the run loop to prevent CFRunLoopRun() from exiting. + CFRunLoopSourceContext ctxt = {0, (void*)1 /*must be non-NULL*/, 0, 0, 0, 0, 0, 0, 0, emptyPerform}; + CFRunLoopSourceRef bogusSource = CFRunLoopSourceCreate(0, 0, &ctxt); + CFRunLoopAddSource(loaderRunLoopObject, bogusSource, kCFRunLoopDefaultMode); + + CFRunLoopRun(); + + return 0; +} + +CFRunLoopRef loaderRunLoop() +{ + ASSERT(isMainThread()); + if (!loaderRunLoopObject) { + createThread(runLoaderThread, 0, "WebCore: CFNetwork Loader"); + while (!loaderRunLoopObject) { + // FIXME: Sleep 10? that can't be right... + Sleep(10); + } + } + return loaderRunLoopObject; +} + +} // namespace WebCore + +#endif // USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/cf/LoaderRunLoopCF.h b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.h new file mode 100644 index 0000000..f66128c --- /dev/null +++ b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2009 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 LoaderRunLoopCF_h +#define LoaderRunLoopCF_h + +#if USE(CFNETWORK) + +#if !PLATFORM(WIN) +#error This code is not needed on platforms other than Windows, because main thread's CFRunLoop can be used. +#endif + +namespace WebCore { + +CFRunLoopRef loaderRunLoop(); + +} + +#endif // USE(CFNETWORK) + +#endif // LoaderRunLoop_h diff --git a/Source/WebCore/platform/network/cf/ProxyServerCFNet.cpp b/Source/WebCore/platform/network/cf/ProxyServerCFNet.cpp new file mode 100644 index 0000000..3bef808 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/cf/ResourceError.h b/Source/WebCore/platform/network/cf/ResourceError.h new file mode 100644 index 0000000..aae9a4a --- /dev/null +++ b/Source/WebCore/platform/network/cf/ResourceError.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceError_h +#define ResourceError_h + +#include "ResourceErrorBase.h" + +#include <wtf/RetainPtr.h> +#if USE(CFNETWORK) +#include <CoreFoundation/CFStream.h> +#else +#ifdef __OBJC__ +@class NSError; +#else +class NSError; +#endif +#endif + +namespace WebCore { + +class ResourceError : public ResourceErrorBase { +public: + ResourceError() + : m_dataIsUpToDate(true) + { + } + + ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) + : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) + , m_dataIsUpToDate(true) + { + } + +#if USE(CFNETWORK) + ResourceError(CFStreamError error); + + ResourceError(CFErrorRef error) + : m_dataIsUpToDate(false) + , m_platformError(error) + { + m_isNull = !error; + } + + operator CFErrorRef() const; + operator CFStreamError() const; +#else + ResourceError(NSError* error) + : m_dataIsUpToDate(false) + , m_platformError(error) + { + m_isNull = !error; + } + + operator NSError*() const; +#endif + +private: + friend class ResourceErrorBase; + + void platformLazyInit(); + static bool platformCompare(const ResourceError& a, const ResourceError& b); + + bool m_dataIsUpToDate; +#if USE(CFNETWORK) + mutable RetainPtr<CFErrorRef> m_platformError; +#else + mutable RetainPtr<NSError> m_platformError; +#endif +}; + +} // namespace WebCore + +#endif // ResourceError_h diff --git a/Source/WebCore/platform/network/cf/ResourceErrorCF.cpp b/Source/WebCore/platform/network/cf/ResourceErrorCF.cpp new file mode 100644 index 0000000..1eba97e --- /dev/null +++ b/Source/WebCore/platform/network/cf/ResourceErrorCF.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 "KURL.h" +#include "ResourceError.h" + +#if USE(CFNETWORK) + +// FIXME: Once <rdar://problem/5050881> is fixed in open source we +// can remove this extern "C" +extern "C" { +#include <CFNetwork/CFNetworkErrors.h> +} + +#include <CoreFoundation/CFError.h> +#include <WTF/RetainPtr.h> + +namespace WebCore { + +const CFStringRef failingURLStringKey = CFSTR("NSErrorFailingURLStringKey"); +const CFStringRef failingURLKey = CFSTR("NSErrorFailingURLKey"); + +// FIXME: Once <rdar://problem/5050841> is fixed we can remove this constructor. +ResourceError::ResourceError(CFStreamError error) + : m_dataIsUpToDate(true) +{ + m_isNull = false; + m_errorCode = error.error; + + switch(error.domain) { + case kCFStreamErrorDomainCustom: + m_domain ="NSCustomErrorDomain"; + break; + case kCFStreamErrorDomainPOSIX: + m_domain = "NSPOSIXErrorDomain"; + break; + case kCFStreamErrorDomainMacOSStatus: + m_domain = "NSOSStatusErrorDomain"; + break; + } +} + +void ResourceError::platformLazyInit() +{ + if (m_dataIsUpToDate) + return; + + if (!m_platformError) + return; + + CFStringRef domain = CFErrorGetDomain(m_platformError.get()); + if (domain == kCFErrorDomainMach || domain == kCFErrorDomainCocoa) + m_domain ="NSCustomErrorDomain"; + else if (domain == kCFErrorDomainCFNetwork) + m_domain = "CFURLErrorDomain"; + else if (domain == kCFErrorDomainPOSIX) + m_domain = "NSPOSIXErrorDomain"; + else if (domain == kCFErrorDomainOSStatus) + m_domain = "NSOSStatusErrorDomain"; + else if (domain == kCFErrorDomainWinSock) + m_domain = "kCFErrorDomainWinSock"; + + m_errorCode = CFErrorGetCode(m_platformError.get()); + + RetainPtr<CFDictionaryRef> userInfo(AdoptCF, CFErrorCopyUserInfo(m_platformError.get())); + if (userInfo.get()) { + CFStringRef failingURLString = (CFStringRef) CFDictionaryGetValue(userInfo.get(), failingURLStringKey); + if (failingURLString) + m_failingURL = String(failingURLString); + else { + CFURLRef failingURL = (CFURLRef) CFDictionaryGetValue(userInfo.get(), failingURLKey); + if (failingURL) { + RetainPtr<CFURLRef> absoluteURLRef(AdoptCF, CFURLCopyAbsoluteURL(failingURL)); + if (absoluteURLRef.get()) { + failingURLString = CFURLGetString(absoluteURLRef.get()); + m_failingURL = String(failingURLString); + } + } + } + m_localizedDescription = (CFStringRef) CFDictionaryGetValue(userInfo.get(), kCFErrorLocalizedDescriptionKey); + } + + m_dataIsUpToDate = true; +} + +bool ResourceError::platformCompare(const ResourceError& a, const ResourceError& b) +{ + return (CFErrorRef)a == (CFErrorRef)b; +} + +ResourceError::operator CFErrorRef() const +{ + if (m_isNull) { + ASSERT(!m_platformError); + return 0; + } + + if (!m_platformError) { + RetainPtr<CFMutableDictionaryRef> userInfo(AdoptCF, CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + + if (!m_localizedDescription.isEmpty()) { + RetainPtr<CFStringRef> localizedDescriptionString(AdoptCF, m_localizedDescription.createCFString()); + CFDictionarySetValue(userInfo.get(), kCFErrorLocalizedDescriptionKey, localizedDescriptionString.get()); + } + + if (!m_failingURL.isEmpty()) { + RetainPtr<CFStringRef> failingURLString(AdoptCF, m_failingURL.createCFString()); + CFDictionarySetValue(userInfo.get(), failingURLStringKey, failingURLString.get()); + RetainPtr<CFURLRef> url(AdoptCF, KURL(ParsedURLString, m_failingURL).createCFURL()); + CFDictionarySetValue(userInfo.get(), failingURLKey, url.get()); + } + + RetainPtr<CFStringRef> domainString(AdoptCF, m_domain.createCFString()); + m_platformError.adoptCF(CFErrorCreate(0, domainString.get(), m_errorCode, userInfo.get())); + } + + return m_platformError.get(); +} + +ResourceError::operator CFStreamError() const +{ + lazyInit(); + + CFStreamError result; + result.error = m_errorCode; + + if (m_domain == "NSCustomErrorDomain") + result.domain = kCFStreamErrorDomainCustom; + else if (m_domain == "NSPOSIXErrorDomain") + result.domain = kCFStreamErrorDomainPOSIX; + else if (m_domain == "NSOSStatusErrorDomain") + result.domain = kCFStreamErrorDomainMacOSStatus; + else + ASSERT_NOT_REACHED(); + + return result; +} + +} // namespace WebCore + +#endif // USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp b/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp new file mode 100644 index 0000000..66ae5a0 --- /dev/null +++ b/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp @@ -0,0 +1,773 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007, 2009 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 COMPUTER, 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 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" + +#if USE(CFNETWORK) + +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "ResourceHandleInternal.h" + +#include "AuthenticationCF.h" +#include "AuthenticationChallenge.h" +#include "Base64.h" +#include "CookieStorageCFNet.h" +#include "CredentialStorage.h" +#include "CachedResourceLoader.h" +#include "FormDataStreamCFNet.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "LoaderRunLoopCF.h" +#include "Logging.h" +#include "MIMETypeRegistry.h" +#include "ResourceError.h" +#include "ResourceResponse.h" +#include "SharedBuffer.h" +#include <CFNetwork/CFNetwork.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#include <process.h> // for _beginthread() +#include <sys/stat.h> +#include <sys/types.h> +#include <wtf/HashMap.h> +#include <wtf/Threading.h> +#include <wtf/text/CString.h> + +// FIXME: Remove this declaration once it's in WebKitSupportLibrary. +extern "C" { +__declspec(dllimport) CFURLConnectionRef CFURLConnectionCreateWithProperties( + CFAllocatorRef alloc, + CFURLRequestRef request, + CFURLConnectionClient * client, + CFDictionaryRef properties); +} + +namespace WebCore { + +static CFStringRef WebCoreSynchronousLoaderRunLoopMode = CFSTR("WebCoreSynchronousLoaderRunLoopMode"); + +class WebCoreSynchronousLoaderClient : public ResourceHandleClient { +public: + static PassOwnPtr<WebCoreSynchronousLoaderClient> create(ResourceResponse& response, ResourceError& error) + { + return adoptPtr(new WebCoreSynchronousLoaderClient(response, error)); + } + + void setAllowStoredCredentials(bool allow) { m_allowStoredCredentials = allow; } + bool isDone() { return m_isDone; } + + CFMutableDataRef data() { return m_data.get(); } + +private: + WebCoreSynchronousLoaderClient(ResourceResponse& response, ResourceError& error) + : m_allowStoredCredentials(false) + , m_response(response) + , m_error(error) + , m_isDone(false) + { + } + + virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/); + virtual bool shouldUseCredentialStorage(ResourceHandle*); + virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge&); + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int /*lengthReceived*/); + virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/); + virtual void didFail(ResourceHandle*, const ResourceError&); + + bool m_allowStoredCredentials; + ResourceResponse& m_response; + RetainPtr<CFMutableDataRef> m_data; + ResourceError& m_error; + bool m_isDone; +}; + +static HashSet<String>& allowsAnyHTTPSCertificateHosts() +{ + static HashSet<String> hosts; + + return hosts; +} + +static HashMap<String, RetainPtr<CFDataRef> >& clientCerts() +{ + static HashMap<String, RetainPtr<CFDataRef> > certs; + return certs; +} + +static void setDefaultMIMEType(CFURLResponseRef response) +{ + static CFStringRef defaultMIMETypeString = defaultMIMEType().createCFString(); + + CFURLResponseSetMIMEType(response, defaultMIMETypeString); +} + +static String encodeBasicAuthorization(const String& user, const String& password) +{ + CString unencodedString = (user + ":" + password).utf8(); + Vector<char> unencoded(unencodedString.length()); + std::copy(unencodedString.data(), unencodedString.data() + unencodedString.length(), unencoded.begin()); + Vector<char> encoded; + base64Encode(unencoded, encoded); + return String(encoded.data(), encoded.size()); +} + +CFURLRequestRef willSendRequest(CFURLConnectionRef conn, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); + + if (!cfRedirectResponse) { + CFRetain(cfRequest); + return cfRequest; + } + + LOG(Network, "CFNet - willSendRequest(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data()); + + ResourceRequest request; + if (cfRedirectResponse) { + CFHTTPMessageRef httpMessage = CFURLResponseGetHTTPResponse(cfRedirectResponse); + if (httpMessage && CFHTTPMessageGetResponseStatusCode(httpMessage) == 307) { + RetainPtr<CFStringRef> lastHTTPMethod(AdoptCF, handle->lastHTTPMethod().createCFString()); + RetainPtr<CFStringRef> newMethod(AdoptCF, CFURLRequestCopyHTTPRequestMethod(cfRequest)); + if (CFStringCompareWithOptions(lastHTTPMethod.get(), newMethod.get(), CFRangeMake(0, CFStringGetLength(lastHTTPMethod.get())), kCFCompareCaseInsensitive)) { + RetainPtr<CFMutableURLRequestRef> mutableRequest(AdoptCF, CFURLRequestCreateMutableCopy(0, cfRequest)); + CFURLRequestSetHTTPRequestMethod(mutableRequest.get(), lastHTTPMethod.get()); + + FormData* body = handle->firstRequest().httpBody(); + if (!equalIgnoringCase(handle->firstRequest().httpMethod(), "GET") && body && !body->isEmpty()) + WebCore::setHTTPBody(mutableRequest.get(), body); + + String originalContentType = handle->firstRequest().httpContentType(); + RetainPtr<CFStringRef> originalContentTypeCF(AdoptCF, originalContentType.createCFString()); + if (!originalContentType.isEmpty()) + CFURLRequestSetHTTPHeaderFieldValue(mutableRequest.get(), CFSTR("Content-Type"), originalContentTypeCF.get()); + + request = mutableRequest.get(); + } + } + } + if (request.isNull()) + request = cfRequest; + + // Should not set Referer after a redirect from a secure resource to non-secure one. + if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https")) + request.clearHTTPReferrer(); + + handle->willSendRequest(request, cfRedirectResponse); + + if (request.isNull()) + return 0; + + cfRequest = request.cfURLRequest(); + + CFRetain(cfRequest); + return cfRequest; +} + +void didReceiveResponse(CFURLConnectionRef conn, CFURLResponseRef cfResponse, const void* clientInfo) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); + + LOG(Network, "CFNet - didReceiveResponse(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data()); + + if (!handle->client()) + return; + + if (!CFURLResponseGetMIMEType(cfResponse)) { + // We should never be applying the default MIMEType if we told the networking layer to do content sniffing for handle. + ASSERT(!handle->shouldContentSniff()); + setDefaultMIMEType(cfResponse); + } + + handle->client()->didReceiveResponse(handle, cfResponse); +} + +void didReceiveData(CFURLConnectionRef conn, CFDataRef data, CFIndex originalLength, const void* clientInfo) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); + const UInt8* bytes = CFDataGetBytePtr(data); + CFIndex length = CFDataGetLength(data); + + LOG(Network, "CFNet - didReceiveData(conn=%p, handle=%p, bytes=%d) (%s)", conn, handle, length, handle->firstRequest().url().string().utf8().data()); + + if (handle->client()) + handle->client()->didReceiveData(handle, (const char*)bytes, length, originalLength); +} + +static void didSendBodyData(CFURLConnectionRef conn, CFIndex bytesWritten, CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite, const void *clientInfo) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); + if (!handle || !handle->client()) + return; + handle->client()->didSendData(handle, totalBytesWritten, totalBytesExpectedToWrite); +} + +static Boolean shouldUseCredentialStorageCallback(CFURLConnectionRef conn, const void* clientInfo) +{ + ResourceHandle* handle = const_cast<ResourceHandle*>(static_cast<const ResourceHandle*>(clientInfo)); + + LOG(Network, "CFNet - shouldUseCredentialStorage(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data()); + + if (!handle) + return false; + + return handle->shouldUseCredentialStorage(); +} + +void didFinishLoading(CFURLConnectionRef conn, const void* clientInfo) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); + + LOG(Network, "CFNet - didFinishLoading(conn=%p, handle=%p) (%s)", conn, handle, handle->firstRequest().url().string().utf8().data()); + + if (handle->client()) + handle->client()->didFinishLoading(handle, 0); +} + +void didFail(CFURLConnectionRef conn, CFErrorRef error, const void* clientInfo) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); + + LOG(Network, "CFNet - didFail(conn=%p, handle=%p, error = %p) (%s)", conn, handle, error, handle->firstRequest().url().string().utf8().data()); + + if (handle->client()) + handle->client()->didFail(handle, ResourceError(error)); +} + +CFCachedURLResponseRef willCacheResponse(CFURLConnectionRef conn, CFCachedURLResponseRef cachedResponse, const void* clientInfo) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); + + if (handle->client() && !handle->client()->shouldCacheResponse(handle, cachedResponse)) + return 0; + + CacheStoragePolicy policy = static_cast<CacheStoragePolicy>(CFCachedURLResponseGetStoragePolicy(cachedResponse)); + + if (handle->client()) + handle->client()->willCacheResponse(handle, policy); + + if (static_cast<CFURLCacheStoragePolicy>(policy) != CFCachedURLResponseGetStoragePolicy(cachedResponse)) + cachedResponse = CFCachedURLResponseCreateWithUserInfo(kCFAllocatorDefault, + CFCachedURLResponseGetWrappedResponse(cachedResponse), + CFCachedURLResponseGetReceiverData(cachedResponse), + CFCachedURLResponseGetUserInfo(cachedResponse), + static_cast<CFURLCacheStoragePolicy>(policy)); + CFRetain(cachedResponse); + + return cachedResponse; +} + +void didReceiveChallenge(CFURLConnectionRef conn, CFURLAuthChallengeRef challenge, const void* clientInfo) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(const_cast<void*>(clientInfo)); + ASSERT(handle); + LOG(Network, "CFNet - didReceiveChallenge(conn=%p, handle=%p (%s)", conn, handle, handle->firstRequest().url().string().utf8().data()); + + handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(challenge, handle)); +} + +void addHeadersFromHashMap(CFMutableURLRequestRef request, const HTTPHeaderMap& requestHeaders) +{ + if (!requestHeaders.size()) + return; + + HTTPHeaderMap::const_iterator end = requestHeaders.end(); + for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) { + CFStringRef key = it->first.createCFString(); + CFStringRef value = it->second.createCFString(); + CFURLRequestSetHTTPHeaderFieldValue(request, key, value); + CFRelease(key); + CFRelease(value); + } +} + +ResourceHandleInternal::~ResourceHandleInternal() +{ + if (m_connection) { + LOG(Network, "CFNet - Cancelling connection %p (%s)", m_connection, m_firstRequest.url().string().utf8().data()); + CFURLConnectionCancel(m_connection.get()); + } +} + +ResourceHandle::~ResourceHandle() +{ + LOG(Network, "CFNet - Destroying job %p (%s)", this, d->m_firstRequest.url().string().utf8().data()); +} + +CFArrayRef arrayFromFormData(const FormData& d) +{ + size_t size = d.elements().size(); + CFMutableArrayRef a = CFArrayCreateMutable(0, d.elements().size(), &kCFTypeArrayCallBacks); + for (size_t i = 0; i < size; ++i) { + const FormDataElement& e = d.elements()[i]; + if (e.m_type == FormDataElement::data) { + CFDataRef data = CFDataCreate(0, (const UInt8*)e.m_data.data(), e.m_data.size()); + CFArrayAppendValue(a, data); + CFRelease(data); + } else { + ASSERT(e.m_type == FormDataElement::encodedFile); + CFStringRef filename = e.m_filename.createCFString(); + CFArrayAppendValue(a, filename); + CFRelease(filename); + } + } + return a; +} + +static CFURLRequestRef makeFinalRequest(const ResourceRequest& request, bool shouldContentSniff) +{ + CFMutableURLRequestRef newRequest = CFURLRequestCreateMutableCopy(kCFAllocatorDefault, request.cfURLRequest()); + + if (!shouldContentSniff) + wkSetCFURLRequestShouldContentSniff(newRequest, false); + + RetainPtr<CFMutableDictionaryRef> sslProps; + + if (allowsAnyHTTPSCertificateHosts().contains(request.url().host().lower())) { + sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue); + CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredRoots, kCFBooleanTrue); + CFDictionaryAddValue(sslProps.get(), kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue); + CFDictionaryAddValue(sslProps.get(), kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse); + } + + HashMap<String, RetainPtr<CFDataRef> >::iterator clientCert = clientCerts().find(request.url().host().lower()); + if (clientCert != clientCerts().end()) { + if (!sslProps) + sslProps.adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + wkSetClientCertificateInSSLProperties(sslProps.get(), (clientCert->second).get()); + } + + if (sslProps) + CFURLRequestSetSSLProperties(newRequest, sslProps.get()); + + if (CFHTTPCookieStorageRef cookieStorage = currentCookieStorage()) { + CFURLRequestSetHTTPCookieStorage(newRequest, cookieStorage); + CFHTTPCookieStorageAcceptPolicy policy = CFHTTPCookieStorageGetCookieAcceptPolicy(cookieStorage); + CFURLRequestSetHTTPCookieStorageAcceptPolicy(newRequest, policy); + + // If a URL already has cookies, then we'll relax the 3rd party cookie policy and accept new cookies. + if (policy == CFHTTPCookieStorageAcceptPolicyOnlyFromMainDocumentDomain) { + CFURLRef url = CFURLRequestGetURL(newRequest); + RetainPtr<CFArrayRef> cookies(AdoptCF, CFHTTPCookieStorageCopyCookiesForURL(cookieStorage, url, false)); + if (CFArrayGetCount(cookies.get())) + CFURLRequestSetMainDocumentURL(newRequest, url); + } + } + + return newRequest; +} + +static CFDictionaryRef createConnectionProperties(bool shouldUseCredentialStorage) +{ + static const CFStringRef webKitPrivateSessionCF = CFSTR("WebKitPrivateSession"); + static const CFStringRef _kCFURLConnectionSessionID = CFSTR("_kCFURLConnectionSessionID"); + static const CFStringRef kCFURLConnectionSocketStreamProperties = CFSTR("kCFURLConnectionSocketStreamProperties"); + + CFDictionaryRef sessionID = shouldUseCredentialStorage ? + CFDictionaryCreate(0, 0, 0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) : + CFDictionaryCreate(0, (const void**)&_kCFURLConnectionSessionID, (const void**)&webKitPrivateSessionCF, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFDictionaryRef propertiesDictionary = CFDictionaryCreate(0, (const void**)&kCFURLConnectionSocketStreamProperties, (const void**)&sessionID, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFRelease(sessionID); + return propertiesDictionary; +} + +void ResourceHandle::createCFURLConnection(bool shouldUseCredentialStorage, bool shouldContentSniff) +{ + if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) && !firstRequest().url().protocolInHTTPFamily()) { + // Credentials for ftp can only be passed in URL, the didReceiveAuthenticationChallenge delegate call won't be made. + KURL urlWithCredentials(firstRequest().url()); + urlWithCredentials.setUser(d->m_user); + urlWithCredentials.setPass(d->m_pass); + firstRequest().setURL(urlWithCredentials); + } + + // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, + // try and reuse the credential preemptively, as allowed by RFC 2617. + if (shouldUseCredentialStorage && firstRequest().url().protocolInHTTPFamily()) { + if (d->m_user.isEmpty() && d->m_pass.isEmpty()) { + // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, + // try and reuse the credential preemptively, as allowed by RFC 2617. + d->m_initialCredential = CredentialStorage::get(firstRequest().url()); + } else { + // If there is already a protection space known for the URL, update stored credentials before sending a request. + // This makes it possible to implement logout by sending an XMLHttpRequest with known incorrect credentials, and aborting it immediately + // (so that an authentication dialog doesn't pop up). + CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), firstRequest().url()); + } + } + + if (!d->m_initialCredential.isEmpty()) { + String authHeader = "Basic " + encodeBasicAuthorization(d->m_initialCredential.user(), d->m_initialCredential.password()); + firstRequest().addHTTPHeaderField("Authorization", authHeader); + } + + RetainPtr<CFURLRequestRef> request(AdoptCF, makeFinalRequest(firstRequest(), shouldContentSniff)); + + CFURLConnectionClient_V3 client = { 3, this, 0, 0, 0, WebCore::willSendRequest, didReceiveResponse, didReceiveData, 0, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0}; + RetainPtr<CFDictionaryRef> connectionProperties(AdoptCF, createConnectionProperties(shouldUseCredentialStorage)); + + d->m_connection.adoptCF(CFURLConnectionCreateWithProperties(0, request.get(), reinterpret_cast<CFURLConnectionClient*>(&client), connectionProperties.get())); +} + +bool ResourceHandle::start(NetworkingContext* context) +{ + if (!context) + return false; + + // If NetworkingContext is invalid then we are no longer attached to a Page, + // this must be an attempted load from an unload handler, so let's just block it. + if (!context->isValid()) + return false; + + bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this); + + createCFURLConnection(shouldUseCredentialStorage, d->m_shouldContentSniff); + + CFURLConnectionScheduleWithCurrentMessageQueue(d->m_connection.get()); + CFURLConnectionScheduleDownloadWithRunLoop(d->m_connection.get(), loaderRunLoop(), kCFRunLoopDefaultMode); + CFURLConnectionStart(d->m_connection.get()); + + LOG(Network, "CFNet - Starting URL %s (handle=%p, conn=%p)", firstRequest().url().string().utf8().data(), this, d->m_connection); + + return true; +} + +void ResourceHandle::cancel() +{ + if (d->m_connection) { + CFURLConnectionCancel(d->m_connection.get()); + d->m_connection = 0; + } +} + +PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() +{ + ASSERT_NOT_REACHED(); + return 0; +} + +bool ResourceHandle::supportsBufferedData() +{ + return false; +} + +void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse) +{ + const KURL& url = request.url(); + d->m_user = url.user(); + d->m_pass = url.pass(); + d->m_lastHTTPMethod = request.httpMethod(); + request.removeCredentials(); + if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) + request.clearHTTPAuthorization(); + + client()->willSendRequest(this, request, redirectResponse); +} + +bool ResourceHandle::shouldUseCredentialStorage() +{ + LOG(Network, "CFNet - shouldUseCredentialStorage()"); + if (client()) + return client()->shouldUseCredentialStorage(this); + + return false; +} + +void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge) +{ + LOG(Network, "CFNet - didReceiveAuthenticationChallenge()"); + ASSERT(d->m_currentWebChallenge.isNull()); + // Since CFURLConnection networking relies on keeping a reference to the original CFURLAuthChallengeRef, + // we make sure that is actually present + ASSERT(challenge.cfURLAuthChallengeRef()); + ASSERT(challenge.authenticationClient() == this); // Should be already set. + + if (!d->m_user.isNull() && !d->m_pass.isNull()) { + RetainPtr<CFStringRef> user(AdoptCF, d->m_user.createCFString()); + RetainPtr<CFStringRef> pass(AdoptCF, d->m_pass.createCFString()); + RetainPtr<CFURLCredentialRef> credential(AdoptCF, + CFURLCredentialCreate(kCFAllocatorDefault, user.get(), pass.get(), 0, kCFURLCredentialPersistenceNone)); + + KURL urlToStore; + if (challenge.failureResponse().httpStatusCode() == 401) + urlToStore = firstRequest().url(); + CredentialStorage::set(core(credential.get()), challenge.protectionSpace(), urlToStore); + + CFURLConnectionUseCredential(d->m_connection.get(), credential.get(), challenge.cfURLAuthChallengeRef()); + d->m_user = String(); + d->m_pass = String(); + // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly. + return; + } + + if (!client() || client()->shouldUseCredentialStorage(this)) { + if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) { + // The stored credential wasn't accepted, stop using it. + // There is a race condition here, since a different credential might have already been stored by another ResourceHandle, + // but the observable effect should be very minor, if any. + CredentialStorage::remove(challenge.protectionSpace()); + } + + if (!challenge.previousFailureCount()) { + Credential credential = CredentialStorage::get(challenge.protectionSpace()); + if (!credential.isEmpty() && credential != d->m_initialCredential) { + ASSERT(credential.persistence() == CredentialPersistenceNone); + if (challenge.failureResponse().httpStatusCode() == 401) { + // Store the credential back, possibly adding it as a default for this directory. + CredentialStorage::set(credential, challenge.protectionSpace(), firstRequest().url()); + } + RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential)); + CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef()); + return; + } + } + } + + d->m_currentWebChallenge = challenge; + + if (client()) + client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge); +} + +void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential) +{ + LOG(Network, "CFNet - receivedCredential()"); + ASSERT(!challenge.isNull()); + ASSERT(challenge.cfURLAuthChallengeRef()); + if (challenge != d->m_currentWebChallenge) + return; + + // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map. + if (credential.isEmpty()) { + receivedRequestToContinueWithoutCredential(challenge); + return; + } + + if (credential.persistence() == CredentialPersistenceForSession) { + // Manage per-session credentials internally, because once NSURLCredentialPersistencePerSession is used, there is no way + // to ignore it for a particular request (short of removing it altogether). + Credential webCredential(credential.user(), credential.password(), CredentialPersistenceNone); + RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(webCredential)); + + KURL urlToStore; + if (challenge.failureResponse().httpStatusCode() == 401) + urlToStore = firstRequest().url(); + CredentialStorage::set(webCredential, challenge.protectionSpace(), urlToStore); + + CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef()); + } else { + RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential)); + CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef()); + } + + clearAuthentication(); +} + +void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge) +{ + LOG(Network, "CFNet - receivedRequestToContinueWithoutCredential()"); + ASSERT(!challenge.isNull()); + ASSERT(challenge.cfURLAuthChallengeRef()); + if (challenge != d->m_currentWebChallenge) + return; + + CFURLConnectionUseCredential(d->m_connection.get(), 0, challenge.cfURLAuthChallengeRef()); + + clearAuthentication(); +} + +void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge) +{ + LOG(Network, "CFNet - receivedCancellation()"); + if (challenge != d->m_currentWebChallenge) + return; + + if (client()) + client()->receivedCancellation(this, challenge); +} + +CFURLConnectionRef ResourceHandle::connection() const +{ + return d->m_connection.get(); +} + +CFURLConnectionRef ResourceHandle::releaseConnectionForDownload() +{ + LOG(Network, "CFNet - Job %p releasing connection %p for download", this, d->m_connection.get()); + return d->m_connection.releaseRef(); +} + +void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& vector) +{ + LOG(Network, "ResourceHandle::loadResourceSynchronously:%s allowStoredCredentials:%u", request.url().string().utf8().data(), storedCredentials); + + ASSERT(!request.isEmpty()); + + ASSERT(response.isNull()); + ASSERT(error.isNull()); + + OwnPtr<WebCoreSynchronousLoaderClient> client = WebCoreSynchronousLoaderClient::create(response, error); + client->setAllowStoredCredentials(storedCredentials == AllowStoredCredentials); + + RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, client.get(), false /*defersLoading*/, true /*shouldContentSniff*/)); + + if (context && handle->d->m_scheduledFailureType != NoFailure) { + error = context->blockedError(request); + return; + } + + RetainPtr<CFDictionaryRef> connectionProperties(AdoptCF, createConnectionProperties(storedCredentials == AllowStoredCredentials)); + + handle->createCFURLConnection(storedCredentials == AllowStoredCredentials, ResourceHandle::shouldContentSniffURL(request.url())); + + CFURLConnectionScheduleWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), WebCoreSynchronousLoaderRunLoopMode); + CFURLConnectionScheduleDownloadWithRunLoop(handle->connection(), CFRunLoopGetCurrent(), WebCoreSynchronousLoaderRunLoopMode); + CFURLConnectionStart(handle->connection()); + + while (!client->isDone()) + CFRunLoopRunInMode(WebCoreSynchronousLoaderRunLoopMode, UINT_MAX, true); + + CFURLConnectionCancel(handle->connection()); + + if (error.isNull() && response.mimeType().isNull()) + setDefaultMIMEType(response.cfURLResponse()); + + RetainPtr<CFDataRef> data = client->data(); + + if (!error.isNull()) { + response = ResourceResponse(request.url(), String(), 0, String(), String()); + + CFErrorRef cfError = error; + CFStringRef domain = CFErrorGetDomain(cfError); + // FIXME: Return the actual response for failed authentication. + if (domain == kCFErrorDomainCFNetwork) + response.setHTTPStatusCode(CFErrorGetCode(cfError)); + else + response.setHTTPStatusCode(404); + } + + if (data) { + ASSERT(vector.isEmpty()); + vector.append(CFDataGetBytePtr(data.get()), CFDataGetLength(data.get())); + } +} + +void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host) +{ + allowsAnyHTTPSCertificateHosts().add(host.lower()); +} + +void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert) +{ + clientCerts().set(host.lower(), cert); +} + +void ResourceHandle::platformSetDefersLoading(bool defers) +{ + if (!d->m_connection) + return; + + if (defers) + CFURLConnectionHalt(d->m_connection.get()); + else + CFURLConnectionResume(d->m_connection.get()); +} + +bool ResourceHandle::loadsBlocked() +{ + return false; +} + +bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame* frame) +{ + request.setCachePolicy(ReturnCacheDataDontLoad); + + CFURLResponseRef cfResponse = 0; + CFErrorRef cfError = 0; + RetainPtr<CFURLRequestRef> cfRequest(AdoptCF, makeFinalRequest(request, true)); + RetainPtr<CFDataRef> data(AdoptCF, CFURLConnectionSendSynchronousRequest(cfRequest.get(), &cfResponse, &cfError, request.timeoutInterval())); + bool cached = cfResponse && !cfError; + + if (cfError) + CFRelease(cfError); + if (cfResponse) + CFRelease(cfResponse); + + return cached; +} + +void WebCoreSynchronousLoaderClient::willSendRequest(ResourceHandle* handle, ResourceRequest& request, const ResourceResponse& /*redirectResponse*/) +{ + // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. + if (!protocolHostAndPortAreEqual(handle->firstRequest().url(), request.url())) { + ASSERT(!m_error); + RetainPtr<CFErrorRef> cfError(AdoptCF, CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainCFNetwork, kCFURLErrorBadServerResponse, 0)); + m_error = cfError.get(); + m_isDone = true; + request = 0; + return; + } +} +void WebCoreSynchronousLoaderClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) +{ + m_response = response; +} + +void WebCoreSynchronousLoaderClient::didReceiveData(ResourceHandle*, const char* data, int length, int /*lengthReceived*/) +{ + if (!m_data) + m_data.adoptCF(CFDataCreateMutable(kCFAllocatorDefault, 0)); + CFDataAppendBytes(m_data.get(), reinterpret_cast<const UInt8*>(data), length); +} + +void WebCoreSynchronousLoaderClient::didFinishLoading(ResourceHandle*, double) +{ + m_isDone = true; +} + +void WebCoreSynchronousLoaderClient::didFail(ResourceHandle*, const ResourceError& error) +{ + m_error = error; + m_isDone = true; +} + +void WebCoreSynchronousLoaderClient::didReceiveAuthenticationChallenge(ResourceHandle* handle, const AuthenticationChallenge& challenge) +{ + // FIXME: The user should be asked for credentials, as in async case. + CFURLConnectionUseCredential(handle->connection(), 0, challenge.cfURLAuthChallengeRef()); +} + +bool WebCoreSynchronousLoaderClient::shouldUseCredentialStorage(ResourceHandle*) +{ + // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden. + return m_allowStoredCredentials; +} + +} // namespace WebCore + +#endif // USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/cf/ResourceRequest.h b/Source/WebCore/platform/network/cf/ResourceRequest.h new file mode 100644 index 0000000..172ebe1 --- /dev/null +++ b/Source/WebCore/platform/network/cf/ResourceRequest.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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 COMPUTER, 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 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 ResourceRequest_h +#define ResourceRequest_h + +#include "ResourceRequestBase.h" + +#include <wtf/RetainPtr.h> +#if USE(CFNETWORK) +typedef const struct _CFURLRequest* CFURLRequestRef; +#else +#ifdef __OBJC__ +@class NSURLRequest; +#else +class NSURLRequest; +#endif +#endif + +namespace WebCore { + + class ResourceRequest : public ResourceRequestBase { + public: + ResourceRequest(const String& url) + : ResourceRequestBase(KURL(ParsedURLString, url), UseProtocolCachePolicy) + { + } + + ResourceRequest(const KURL& url) + : ResourceRequestBase(url, UseProtocolCachePolicy) + { + } + + ResourceRequest(const KURL& url, const String& referrer, ResourceRequestCachePolicy policy = UseProtocolCachePolicy) + : ResourceRequestBase(url, policy) + { + setHTTPReferrer(referrer); + } + + ResourceRequest() + : ResourceRequestBase(KURL(), UseProtocolCachePolicy) + { + } + +#if USE(CFNETWORK) + ResourceRequest(CFURLRequestRef cfRequest) + : ResourceRequestBase() + , m_cfRequest(cfRequest) { } + + CFURLRequestRef cfURLRequest() const; +#else + ResourceRequest(NSURLRequest* nsRequest) + : ResourceRequestBase() + , m_nsRequest(nsRequest) { } + + void applyWebArchiveHackForMail(); + NSURLRequest* nsURLRequest() const; +#endif + + private: + friend class ResourceRequestBase; + + void doUpdatePlatformRequest(); + void doUpdateResourceRequest(); + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } + +#if USE(CFNETWORK) + RetainPtr<CFURLRequestRef> m_cfRequest; +#else + RetainPtr<NSURLRequest> m_nsRequest; +#endif + }; + + struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { + }; + +} // namespace WebCore + +#endif // ResourceRequest_h diff --git a/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp new file mode 100644 index 0000000..410a649 --- /dev/null +++ b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2006, 2007, 2008 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 COMPUTER, 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 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 "ResourceRequestCFNet.h" + +#if USE(CFNETWORK) + +#include "FormDataStreamCFNet.h" +#include "ResourceRequest.h" + +#include <CFNetwork/CFURLRequestPriv.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> + +namespace WebCore { + +typedef void (*CFURLRequestSetContentDispositionEncodingFallbackArrayFunction)(CFMutableURLRequestRef, CFArrayRef); +typedef CFArrayRef (*CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction)(CFURLRequestRef); + +static HMODULE findCFNetworkModule() +{ +#ifndef DEBUG_ALL + return GetModuleHandleA("CFNetwork"); +#else + return GetModuleHandleA("CFNetwork_debug"); +#endif +} + +static CFURLRequestSetContentDispositionEncodingFallbackArrayFunction findCFURLRequestSetContentDispositionEncodingFallbackArrayFunction() +{ + return reinterpret_cast<CFURLRequestSetContentDispositionEncodingFallbackArrayFunction>(GetProcAddress(findCFNetworkModule(), "_CFURLRequestSetContentDispositionEncodingFallbackArray")); +} + +static CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction findCFURLRequestCopyContentDispositionEncodingFallbackArrayFunction() +{ + return reinterpret_cast<CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction>(GetProcAddress(findCFNetworkModule(), "_CFURLRequestCopyContentDispositionEncodingFallbackArray")); +} + +static void setContentDispositionEncodingFallbackArray(CFMutableURLRequestRef request, CFArrayRef fallbackArray) +{ + static CFURLRequestSetContentDispositionEncodingFallbackArrayFunction function = findCFURLRequestSetContentDispositionEncodingFallbackArrayFunction(); + if (function) + function(request, fallbackArray); +} + +static CFArrayRef copyContentDispositionEncodingFallbackArray(CFURLRequestRef request) +{ + static CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction function = findCFURLRequestCopyContentDispositionEncodingFallbackArrayFunction(); + if (!function) + return 0; + return function(request); +} + +CFURLRequestRef ResourceRequest::cfURLRequest() const +{ + updatePlatformRequest(); + + return m_cfRequest.get(); +} + +static inline void setHeaderFields(CFMutableURLRequestRef request, const HTTPHeaderMap& requestHeaders) +{ + // Remove existing headers first, as some of them may no longer be present in the map. + RetainPtr<CFDictionaryRef> oldHeaderFields(AdoptCF, CFURLRequestCopyAllHTTPHeaderFields(request)); + CFIndex oldHeaderFieldCount = CFDictionaryGetCount(oldHeaderFields.get()); + if (oldHeaderFieldCount) { + Vector<CFStringRef> oldHeaderFieldNames(oldHeaderFieldCount); + CFDictionaryGetKeysAndValues(oldHeaderFields.get(), reinterpret_cast<const void**>(&oldHeaderFieldNames[0]), 0); + for (CFIndex i = 0; i < oldHeaderFieldCount; ++i) + CFURLRequestSetHTTPHeaderFieldValue(request, oldHeaderFieldNames[i], 0); + } + + HTTPHeaderMap::const_iterator end = requestHeaders.end(); + for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) { + CFStringRef key = it->first.createCFString(); + CFStringRef value = it->second.createCFString(); + CFURLRequestSetHTTPHeaderFieldValue(request, key, value); + CFRelease(key); + CFRelease(value); + } +} + +void ResourceRequest::doUpdatePlatformRequest() +{ + CFMutableURLRequestRef cfRequest; + + RetainPtr<CFURLRef> url(AdoptCF, ResourceRequest::url().createCFURL()); + RetainPtr<CFURLRef> firstPartyForCookies(AdoptCF, ResourceRequest::firstPartyForCookies().createCFURL()); + if (m_cfRequest) { + cfRequest = CFURLRequestCreateMutableCopy(0, m_cfRequest.get()); + CFURLRequestSetURL(cfRequest, url.get()); + CFURLRequestSetMainDocumentURL(cfRequest, firstPartyForCookies.get()); + CFURLRequestSetCachePolicy(cfRequest, (CFURLRequestCachePolicy)cachePolicy()); + CFURLRequestSetTimeoutInterval(cfRequest, timeoutInterval()); + } else { + cfRequest = CFURLRequestCreateMutable(0, url.get(), (CFURLRequestCachePolicy)cachePolicy(), timeoutInterval(), firstPartyForCookies.get()); + } + + RetainPtr<CFStringRef> requestMethod(AdoptCF, httpMethod().createCFString()); + CFURLRequestSetHTTPRequestMethod(cfRequest, requestMethod.get()); + + setHeaderFields(cfRequest, httpHeaderFields()); + WebCore::setHTTPBody(cfRequest, httpBody()); + CFURLRequestSetShouldHandleHTTPCookies(cfRequest, allowCookies()); + + unsigned fallbackCount = m_responseContentDispositionEncodingFallbackArray.size(); + RetainPtr<CFMutableArrayRef> encodingFallbacks(AdoptCF, CFArrayCreateMutable(kCFAllocatorDefault, fallbackCount, 0)); + for (unsigned i = 0; i != fallbackCount; ++i) { + RetainPtr<CFStringRef> encodingName(AdoptCF, m_responseContentDispositionEncodingFallbackArray[i].createCFString()); + CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding(encodingName.get()); + if (encoding != kCFStringEncodingInvalidId) + CFArrayAppendValue(encodingFallbacks.get(), reinterpret_cast<const void*>(encoding)); + } + setContentDispositionEncodingFallbackArray(cfRequest, encodingFallbacks.get()); + + if (m_cfRequest) { + RetainPtr<CFHTTPCookieStorageRef> cookieStorage(AdoptCF, CFURLRequestCopyHTTPCookieStorage(m_cfRequest.get())); + if (cookieStorage) + CFURLRequestSetHTTPCookieStorage(cfRequest, cookieStorage.get()); + CFURLRequestSetHTTPCookieStorageAcceptPolicy(cfRequest, CFURLRequestGetHTTPCookieStorageAcceptPolicy(m_cfRequest.get())); + CFURLRequestSetSSLProperties(cfRequest, CFURLRequestGetSSLProperties(m_cfRequest.get())); + } + + m_cfRequest.adoptCF(cfRequest); +} + +void ResourceRequest::doUpdateResourceRequest() +{ + if (!m_cfRequest) { + *this = ResourceRequest(); + return; + } + + m_url = CFURLRequestGetURL(m_cfRequest.get()); + + m_cachePolicy = (ResourceRequestCachePolicy)CFURLRequestGetCachePolicy(m_cfRequest.get()); + m_timeoutInterval = CFURLRequestGetTimeoutInterval(m_cfRequest.get()); + m_firstPartyForCookies = CFURLRequestGetMainDocumentURL(m_cfRequest.get()); + if (CFStringRef method = CFURLRequestCopyHTTPRequestMethod(m_cfRequest.get())) { + m_httpMethod = method; + CFRelease(method); + } + m_allowCookies = CFURLRequestShouldHandleHTTPCookies(m_cfRequest.get()); + + m_httpHeaderFields.clear(); + if (CFDictionaryRef headers = CFURLRequestCopyAllHTTPHeaderFields(m_cfRequest.get())) { + CFIndex headerCount = CFDictionaryGetCount(headers); + Vector<const void*, 128> keys(headerCount); + Vector<const void*, 128> values(headerCount); + CFDictionaryGetKeysAndValues(headers, keys.data(), values.data()); + for (int i = 0; i < headerCount; ++i) + m_httpHeaderFields.set((CFStringRef)keys[i], (CFStringRef)values[i]); + CFRelease(headers); + } + + m_responseContentDispositionEncodingFallbackArray.clear(); + RetainPtr<CFArrayRef> encodingFallbacks(AdoptCF, copyContentDispositionEncodingFallbackArray(m_cfRequest.get())); + if (encodingFallbacks) { + CFIndex count = CFArrayGetCount(encodingFallbacks.get()); + for (CFIndex i = 0; i < count; ++i) { + CFStringEncoding encoding = reinterpret_cast<CFIndex>(CFArrayGetValueAtIndex(encodingFallbacks.get(), i)); + if (encoding != kCFStringEncodingInvalidId) + m_responseContentDispositionEncodingFallbackArray.append(CFStringConvertEncodingToIANACharSetName(encoding)); + } + } + + m_httpBody = httpBodyFromRequest(m_cfRequest.get()); +} + +unsigned initializeMaximumHTTPConnectionCountPerHost() +{ + static const unsigned preferredConnectionCount = 6; + return wkInitializeMaximumHTTPConnectionCountPerHost(preferredConnectionCount); +} + +} // namespace WebCore + +#endif // USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/cf/ResourceRequestCFNet.h b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.h new file mode 100644 index 0000000..39587a4 --- /dev/null +++ b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceRequestCFNet_h +#define ResourceRequestCFNet_h + +#if USE(CFNETWORK) + +typedef const struct _CFURLRequest* CFURLRequestRef; + +namespace WebCore { + + class ResourceRequest; + + void getResourceRequest(ResourceRequest&, CFURLRequestRef); + CFURLRequestRef cfURLRequest(const ResourceRequest&); +} + +#endif // USE(CFNETWORK) + +#endif // ResourceRequestCFNet_h diff --git a/Source/WebCore/platform/network/cf/ResourceResponse.h b/Source/WebCore/platform/network/cf/ResourceResponse.h new file mode 100644 index 0000000..33b6ddc --- /dev/null +++ b/Source/WebCore/platform/network/cf/ResourceResponse.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceResponse_h +#define ResourceResponse_h + +#include "ResourceResponseBase.h" +#include <wtf/RetainPtr.h> + +#if USE(CFNETWORK) +typedef struct _CFURLResponse* CFURLResponseRef; +#else +#ifdef __OBJC__ +@class NSURLResponse; +#else +class NSURLResponse; +#endif +#endif + +namespace WebCore { + +class ResourceResponse : public ResourceResponseBase { +public: + ResourceResponse() + : m_isUpToDate(true) + { + } + +#if USE(CFNETWORK) + ResourceResponse(CFURLResponseRef cfResponse) + : m_cfResponse(cfResponse) + , m_isUpToDate(false) + { + m_isNull = !cfResponse; + } +#else + ResourceResponse(NSURLResponse* nsResponse) + : m_nsResponse(nsResponse) + , m_isUpToDate(false) + { + m_isNull = !nsResponse; + } +#endif + + ResourceResponse(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) + : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename) + , m_isUpToDate(true) + { + } + + unsigned memoryUsage() const + { + // FIXME: Find some programmatic lighweight way to calculate ResourceResponse and associated classes. + // This is a rough estimate of resource overhead based on stats collected from the stress test. + return 3072; + /* 1280 * 2 + // average size of ResourceResponse. Doubled to account for the WebCore copy and the CF copy. + // Mostly due to the size of the hash maps, the Header Map strings and the URL. + 256 * 2 // Overhead from ResourceRequest, doubled to account for WebCore copy and CF copy. + // Mostly due to the URL and Header Map. + */ + } + +#if USE(CFNETWORK) + CFURLResponseRef cfURLResponse() const; +#else + NSURLResponse *nsURLResponse() const; +#endif + +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); + +#if USE(CFNETWORK) + RetainPtr<CFURLResponseRef> m_cfResponse; +#else + RetainPtr<NSURLResponse> m_nsResponse; +#endif + bool m_isUpToDate; +}; + +struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { +}; + +} // namespace WebCore + +#endif // ResourceResponse_h diff --git a/Source/WebCore/platform/network/cf/ResourceResponseCFNet.cpp b/Source/WebCore/platform/network/cf/ResourceResponseCFNet.cpp new file mode 100644 index 0000000..167b079 --- /dev/null +++ b/Source/WebCore/platform/network/cf/ResourceResponseCFNet.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2006, 2007 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 COMPUTER, 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 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" + +#if USE(CFNETWORK) + +#include "HTTPParsers.h" +#include "MIMETypeRegistry.h" +#include <CFNetwork/CFURLResponsePriv.h> +#include <wtf/RetainPtr.h> + +using namespace std; + +// We would like a better value for a maximum time_t, +// but there is no way to do that in C with any certainty. +// INT_MAX should work well enough for our purposes. +#define MAX_TIME_T ((time_t)INT_MAX) + +namespace WebCore { + +CFURLResponseRef ResourceResponse::cfURLResponse() const +{ + return m_cfResponse.get(); +} + +static inline bool filenameHasSaneExtension(const String& filename) +{ + int dot = filename.find('.'); + + // The dot can't be the first or last character in the filename. + int length = filename.length(); + return dot > 0 && dot < length - 1; +} + +static time_t toTimeT(CFAbsoluteTime time) +{ + static const double maxTimeAsDouble = std::numeric_limits<time_t>::max(); + static const double minTimeAsDouble = std::numeric_limits<time_t>::min(); + return min(max(minTimeAsDouble, time + kCFAbsoluteTimeIntervalSince1970), maxTimeAsDouble); +} + +void ResourceResponse::platformLazyInit() +{ + if (m_isUpToDate) + return; + m_isUpToDate = true; + + if (m_isNull) { + ASSERT(!m_cfResponse.get()); + return; + } + + // FIXME: We may need to do MIME type sniffing here (unless that is done in CFURLResponseGetMIMEType). + + m_url = CFURLResponseGetURL(m_cfResponse.get()); + m_mimeType = CFURLResponseGetMIMEType(m_cfResponse.get()); + m_expectedContentLength = CFURLResponseGetExpectedContentLength(m_cfResponse.get()); + m_textEncodingName = CFURLResponseGetTextEncodingName(m_cfResponse.get()); + + // Workaround for <rdar://problem/8757088>, can be removed once that is fixed. + unsigned textEncodingNameLength = m_textEncodingName.length(); + if (textEncodingNameLength >= 2 && m_textEncodingName[0U] == '"' && m_textEncodingName[textEncodingNameLength - 1] == '"') + m_textEncodingName = m_textEncodingName.substring(1, textEncodingNameLength - 2); + + m_lastModifiedDate = toTimeT(CFURLResponseGetLastModifiedDate(m_cfResponse.get())); + + RetainPtr<CFStringRef> suggestedFilename(AdoptCF, CFURLResponseCopySuggestedFilename(m_cfResponse.get())); + m_suggestedFilename = suggestedFilename.get(); + + CFHTTPMessageRef httpResponse = CFURLResponseGetHTTPResponse(m_cfResponse.get()); + if (httpResponse) { + m_httpStatusCode = CFHTTPMessageGetResponseStatusCode(httpResponse); + + RetainPtr<CFStringRef> statusLine(AdoptCF, CFHTTPMessageCopyResponseStatusLine(httpResponse)); + m_httpStatusText = extractReasonPhraseFromHTTPStatusLine(statusLine.get()); + + RetainPtr<CFDictionaryRef> headers(AdoptCF, CFHTTPMessageCopyAllHeaderFields(httpResponse)); + CFIndex headerCount = CFDictionaryGetCount(headers.get()); + Vector<const void*, 128> keys(headerCount); + Vector<const void*, 128> values(headerCount); + CFDictionaryGetKeysAndValues(headers.get(), keys.data(), values.data()); + for (int i = 0; i < headerCount; ++i) + m_httpHeaderFields.set((CFStringRef)keys[i], (CFStringRef)values[i]); + } else + m_httpStatusCode = 0; +} + +bool ResourceResponse::platformCompare(const ResourceResponse& a, const ResourceResponse& b) +{ + return CFEqual(a.cfURLResponse(), b.cfURLResponse()); +} + + +} // namespace WebCore + +#endif // USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/cf/SocketStreamError.h b/Source/WebCore/platform/network/cf/SocketStreamError.h new file mode 100644 index 0000000..5a0ca07 --- /dev/null +++ b/Source/WebCore/platform/network/cf/SocketStreamError.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamError_h +#define SocketStreamError_h + +#include "SocketStreamErrorBase.h" + +namespace WebCore { + +class SocketStreamError : public SocketStreamErrorBase { +public: + SocketStreamError() { } + explicit SocketStreamError(int errorCode) + : SocketStreamErrorBase(errorCode) + { + } + SocketStreamError(int errorCode, const String& failingURL, const String& localizedDescription) + : SocketStreamErrorBase(errorCode, failingURL, localizedDescription) + { + } +}; + +} // namespace WebCore + +#endif // SocketStreamError_h diff --git a/Source/WebCore/platform/network/cf/SocketStreamHandle.h b/Source/WebCore/platform/network/cf/SocketStreamHandle.h new file mode 100644 index 0000000..df1d4a3 --- /dev/null +++ b/Source/WebCore/platform/network/cf/SocketStreamHandle.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamHandle_h +#define SocketStreamHandle_h + +#include "AuthenticationClient.h" +#include "SocketStreamHandleBase.h" +#include <wtf/RetainPtr.h> + + +namespace WebCore { + +class AuthenticationChallenge; +class Credential; +class SocketStreamHandleClient; + +class SocketStreamHandle : public RefCounted<SocketStreamHandle>, public SocketStreamHandleBase, public AuthenticationClient { +public: + static PassRefPtr<SocketStreamHandle> create(const KURL& url, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(url, client)); } + + virtual ~SocketStreamHandle(); + + using RefCounted<SocketStreamHandle>::ref; + using RefCounted<SocketStreamHandle>::deref; + +private: + virtual int platformSend(const char* data, int length); + virtual void platformClose(); + + SocketStreamHandle(const KURL&, SocketStreamHandleClient*); + void createStreams(); + void scheduleStreams(); + void chooseProxy(); +#ifndef BUILDING_ON_TIGER + void chooseProxyFromArray(CFArrayRef); + void executePACFileURL(CFURLRef); + void removePACRunLoopSource(); + RetainPtr<CFRunLoopSourceRef> m_pacRunLoopSource; + static void pacExecutionCallback(void* client, CFArrayRef proxyList, CFErrorRef error); + static void pacExecutionCallbackMainThread(void*); + static CFStringRef copyPACExecutionDescription(void*); +#endif + + bool shouldUseSSL() const { return m_url.protocolIs("wss"); } + + void addCONNECTCredentials(CFHTTPMessageRef response); + + static CFStringRef copyCFStreamDescription(void* ); + static void readStreamCallback(CFReadStreamRef, CFStreamEventType, void*); + static void writeStreamCallback(CFWriteStreamRef, CFStreamEventType, void*); +#if PLATFORM(WIN) + static void readStreamCallbackMainThread(void*); + static void writeStreamCallbackMainThread(void*); +#endif + void readStreamCallback(CFStreamEventType); + void writeStreamCallback(CFStreamEventType); + +#ifndef BUILDING_ON_TIGER + void reportErrorToClient(CFErrorRef); +#endif + + // No authentication for streams per se, but proxy may ask for credentials. + virtual void receivedCredential(const AuthenticationChallenge&, const Credential&); + virtual void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&); + virtual void receivedCancellation(const AuthenticationChallenge&); + + virtual void refAuthenticationClient() { ref(); } + virtual void derefAuthenticationClient() { deref(); } + + enum ConnectingSubstate { New, ExecutingPACFile, WaitingForCredentials, WaitingForConnect, Connected }; + ConnectingSubstate m_connectingSubstate; + + enum ConnectionType { Unknown, Direct, SOCKSProxy, CONNECTProxy }; + ConnectionType m_connectionType; + RetainPtr<CFStringRef> m_proxyHost; + RetainPtr<CFNumberRef> m_proxyPort; + + RetainPtr<CFHTTPMessageRef> m_proxyResponseMessage; + bool m_sentStoredCredentials; + RetainPtr<CFReadStreamRef> m_readStream; + RetainPtr<CFWriteStreamRef> m_writeStream; + + RetainPtr<CFURLRef> m_httpsURL; // ws(s): replaced with https: +}; + +} // namespace WebCore + +#endif // SocketStreamHandle_h diff --git a/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp b/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp new file mode 100644 index 0000000..821b1ca --- /dev/null +++ b/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "SocketStreamHandle.h" + +#include "Credential.h" +#include "CredentialStorage.h" +#include "Logging.h" +#include "ProtectionSpace.h" +#include "SocketStreamError.h" +#include "SocketStreamHandleClient.h" +#include <wtf/MainThread.h> +#include <wtf/text/StringConcatenate.h> + +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) +#include <SystemConfiguration/SystemConfiguration.h> +#endif + +#if PLATFORM(WIN) +#include "LoaderRunLoopCF.h" +#include <WebKitSystemInterface/WebKitSystemInterface.h> +#else +#include "WebCoreSystemInterface.h" +#endif + +#ifdef BUILDING_ON_TIGER +#define CFN_EXPORT extern +#endif + +namespace WebCore { + +SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient* client) + : SocketStreamHandleBase(url, client) + , m_connectingSubstate(New) + , m_connectionType(Unknown) + , m_sentStoredCredentials(false) +{ + LOG(Network, "SocketStreamHandle %p new client %p", this, m_client); + + ASSERT(url.protocolIs("ws") || url.protocolIs("wss")); + + if (!m_url.port()) + m_url.setPort(shouldUseSSL() ? 443 : 80); + + KURL httpsURL(KURL(), "https://" + m_url.host()); + m_httpsURL.adoptCF(httpsURL.createCFURL()); + + createStreams(); + ASSERT(!m_readStream == !m_writeStream); + if (!m_readStream) // Doing asynchronous PAC file processing, streams will be created later. + return; + + scheduleStreams(); +} + +void SocketStreamHandle::scheduleStreams() +{ + ASSERT(m_readStream); + ASSERT(m_writeStream); + + CFStreamClientContext clientContext = { 0, this, 0, 0, copyCFStreamDescription }; + // FIXME: Pass specific events we're interested in instead of -1. + CFReadStreamSetClient(m_readStream.get(), static_cast<CFOptionFlags>(-1), readStreamCallback, &clientContext); + CFWriteStreamSetClient(m_writeStream.get(), static_cast<CFOptionFlags>(-1), writeStreamCallback, &clientContext); + +#if PLATFORM(WIN) + CFReadStreamScheduleWithRunLoop(m_readStream.get(), loaderRunLoop(), kCFRunLoopDefaultMode); + CFWriteStreamScheduleWithRunLoop(m_writeStream.get(), loaderRunLoop(), kCFRunLoopDefaultMode); +#else + CFReadStreamScheduleWithRunLoop(m_readStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + CFWriteStreamScheduleWithRunLoop(m_writeStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes); +#endif + + CFReadStreamOpen(m_readStream.get()); + CFWriteStreamOpen(m_writeStream.get()); + +#ifndef BUILDING_ON_TIGER + if (m_pacRunLoopSource) + removePACRunLoopSource(); +#endif + + m_connectingSubstate = WaitingForConnect; +} + +#ifndef BUILDING_ON_TIGER +CFStringRef SocketStreamHandle::copyPACExecutionDescription(void*) +{ + return CFSTR("WebSocket proxy PAC file execution"); +} + +struct MainThreadPACCallbackInfo { + MainThreadPACCallbackInfo(SocketStreamHandle* handle, CFArrayRef proxyList) : handle(handle), proxyList(proxyList) { } + SocketStreamHandle* handle; + CFArrayRef proxyList; +}; + +void SocketStreamHandle::pacExecutionCallback(void* client, CFArrayRef proxyList, CFErrorRef) +{ + SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(client); + MainThreadPACCallbackInfo info(handle, proxyList); + // If we're already on main thread (e.g. on Mac), callOnMainThreadAndWait() will be just a function call. + callOnMainThreadAndWait(pacExecutionCallbackMainThread, &info); +} + +void SocketStreamHandle::pacExecutionCallbackMainThread(void* invocation) +{ + MainThreadPACCallbackInfo* info = static_cast<MainThreadPACCallbackInfo*>(invocation); + ASSERT(info->handle->m_connectingSubstate == ExecutingPACFile); + // This time, the array won't have PAC as a first entry. + info->handle->chooseProxyFromArray(info->proxyList); + info->handle->createStreams(); + info->handle->scheduleStreams(); +} + +void SocketStreamHandle::executePACFileURL(CFURLRef pacFileURL) +{ + // CFNetwork returns an empty proxy array for WebScoket schemes, so use m_httpsURL. + CFStreamClientContext clientContext = { 0, this, 0, 0, copyPACExecutionDescription }; + m_pacRunLoopSource.adoptCF(CFNetworkExecuteProxyAutoConfigurationURL(pacFileURL, m_httpsURL.get(), pacExecutionCallback, &clientContext)); +#if PLATFORM(WIN) + CFRunLoopAddSource(loaderRunLoop(), m_pacRunLoopSource.get(), kCFRunLoopDefaultMode); +#else + CFRunLoopAddSource(CFRunLoopGetCurrent(), m_pacRunLoopSource.get(), kCFRunLoopCommonModes); +#endif + m_connectingSubstate = ExecutingPACFile; +} + +void SocketStreamHandle::removePACRunLoopSource() +{ + ASSERT(m_pacRunLoopSource); + + CFRunLoopSourceInvalidate(m_pacRunLoopSource.get()); +#if PLATFORM(WIN) + CFRunLoopRemoveSource(loaderRunLoop(), m_pacRunLoopSource.get(), kCFRunLoopDefaultMode); +#else + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_pacRunLoopSource.get(), kCFRunLoopCommonModes); +#endif + m_pacRunLoopSource = 0; +} + +void SocketStreamHandle::chooseProxy() +{ +#ifndef BUILDING_ON_LEOPARD + RetainPtr<CFDictionaryRef> proxyDictionary(AdoptCF, CFNetworkCopySystemProxySettings()); +#else + // We don't need proxy information often, so there is no need to set up a permanent dynamic store session. + RetainPtr<CFDictionaryRef> proxyDictionary(AdoptCF, SCDynamicStoreCopyProxies(0)); +#endif + + // SOCKS or HTTPS (AKA CONNECT) proxies are supported. + // WebSocket protocol relies on handshake being transferred unchanged, so we need a proxy that will not modify headers. + // Since HTTP proxies must add Via headers, they are highly unlikely to work. + // Many CONNECT proxies limit connectivity to port 443, so we prefer SOCKS, if configured. + + if (!proxyDictionary) { + m_connectionType = Direct; + return; + } + + // CFNetworkCopyProxiesForURL doesn't know about WebSocket schemes, so pretend to use http. + // Always use "https" to get HTTPS proxies in result - we'll try to use those for ws:// even though many are configured to reject connections to ports other than 443. + RetainPtr<CFArrayRef> proxyArray(AdoptCF, CFNetworkCopyProxiesForURL(m_httpsURL.get(), proxyDictionary.get())); + + chooseProxyFromArray(proxyArray.get()); +} + +void SocketStreamHandle::chooseProxyFromArray(CFArrayRef proxyArray) +{ + if (!proxyArray) { + m_connectionType = Direct; + return; + } + + CFIndex proxyArrayCount = CFArrayGetCount(proxyArray); + + // PAC is always the first entry, if present. + if (proxyArrayCount) { + CFDictionaryRef proxyInfo = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(proxyArray, 0)); + CFTypeRef proxyType = CFDictionaryGetValue(proxyInfo, kCFProxyTypeKey); + if (proxyType && CFGetTypeID(proxyType) == CFStringGetTypeID()) { + if (CFEqual(proxyType, kCFProxyTypeAutoConfigurationURL)) { + CFTypeRef pacFileURL = CFDictionaryGetValue(proxyInfo, kCFProxyAutoConfigurationURLKey); + if (pacFileURL && CFGetTypeID(pacFileURL) == CFURLGetTypeID()) { + executePACFileURL(static_cast<CFURLRef>(pacFileURL)); + return; + } + } + } + } + + CFDictionaryRef chosenProxy = 0; + for (CFIndex i = 0; i < proxyArrayCount; ++i) { + CFDictionaryRef proxyInfo = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(proxyArray, i)); + CFTypeRef proxyType = CFDictionaryGetValue(proxyInfo, kCFProxyTypeKey); + if (proxyType && CFGetTypeID(proxyType) == CFStringGetTypeID()) { + if (CFEqual(proxyType, kCFProxyTypeSOCKS)) { + m_connectionType = SOCKSProxy; + chosenProxy = proxyInfo; + break; + } + if (CFEqual(proxyType, kCFProxyTypeHTTPS)) { + m_connectionType = CONNECTProxy; + chosenProxy = proxyInfo; + // Keep looking for proxies, as a SOCKS one is preferable. + } + } + } + + if (chosenProxy) { + ASSERT(m_connectionType != Unknown); + ASSERT(m_connectionType != Direct); + + CFTypeRef proxyHost = CFDictionaryGetValue(chosenProxy, kCFProxyHostNameKey); + CFTypeRef proxyPort = CFDictionaryGetValue(chosenProxy, kCFProxyPortNumberKey); + + if (proxyHost && CFGetTypeID(proxyHost) == CFStringGetTypeID() && proxyPort && CFGetTypeID(proxyPort) == CFNumberGetTypeID()) { + m_proxyHost = static_cast<CFStringRef>(proxyHost); + m_proxyPort = static_cast<CFNumberRef>(proxyPort); + return; + } + } + + m_connectionType = Direct; +} + +#else // BUILDING_ON_TIGER + +void SocketStreamHandle::chooseProxy() +{ + // We don't need proxy information often, so there is no need to set up a permanent dynamic store session. + RetainPtr<CFDictionaryRef> proxyDictionary(AdoptCF, SCDynamicStoreCopyProxies(0)); + + // SOCKS or HTTPS (AKA CONNECT) proxies are supported. + // WebSocket protocol relies on handshake being transferred unchanged, so we need a proxy that will not modify headers. + // Since HTTP proxies must add Via headers, they are highly unlikely to work. + // Many CONNECT proxies limit connectivity to port 443, so we prefer SOCKS, if configured. + + if (!proxyDictionary) { + m_connectionType = Direct; + return; + } + + // FIXME: check proxy bypass list and ExcludeSimpleHostnames. + // FIXME: Support PAC files. + + CFTypeRef socksEnableCF = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesSOCKSEnable); + int socksEnable; + if (socksEnableCF && CFGetTypeID(socksEnableCF) == CFNumberGetTypeID() && CFNumberGetValue(static_cast<CFNumberRef>(socksEnableCF), kCFNumberIntType, &socksEnable) && socksEnable) { + CFTypeRef proxyHost = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesSOCKSProxy); + CFTypeRef proxyPort = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesSOCKSPort); + if (proxyHost && CFGetTypeID(proxyHost) == CFStringGetTypeID() && proxyPort && CFGetTypeID(proxyPort) == CFNumberGetTypeID()) { + m_proxyHost = static_cast<CFStringRef>(proxyHost); + m_proxyPort = static_cast<CFNumberRef>(proxyPort); + m_connectionType = SOCKSProxy; + return; + } + } + + CFTypeRef httpsEnableCF = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesHTTPSEnable); + int httpsEnable; + if (httpsEnableCF && CFGetTypeID(httpsEnableCF) == CFNumberGetTypeID() && CFNumberGetValue(static_cast<CFNumberRef>(httpsEnableCF), kCFNumberIntType, &httpsEnable) && httpsEnable) { + CFTypeRef proxyHost = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesHTTPSProxy); + CFTypeRef proxyPort = CFDictionaryGetValue(proxyDictionary.get(), kSCPropNetProxiesHTTPSPort); + + if (proxyHost && CFGetTypeID(proxyHost) == CFStringGetTypeID() && proxyPort && CFGetTypeID(proxyPort) == CFNumberGetTypeID()) { + m_proxyHost = static_cast<CFStringRef>(proxyHost); + m_proxyPort = static_cast<CFNumberRef>(proxyPort); + m_connectionType = CONNECTProxy; + return; + } + } + + m_connectionType = Direct; +} +#endif // BUILDING_ON_TIGER + +void SocketStreamHandle::createStreams() +{ + if (m_connectionType == Unknown) + chooseProxy(); + + // If it's still unknown, then we're resolving a PAC file asynchronously. + if (m_connectionType == Unknown) + return; + + RetainPtr<CFStringRef> host(AdoptCF, m_url.host().createCFString()); + + // Creating streams to final destination, not to proxy. + CFReadStreamRef readStream = 0; + CFWriteStreamRef writeStream = 0; + CFStreamCreatePairWithSocketToHost(0, host.get(), m_url.port(), &readStream, &writeStream); + + m_readStream.adoptCF(readStream); + m_writeStream.adoptCF(writeStream); + + switch (m_connectionType) { + case Unknown: + ASSERT_NOT_REACHED(); + break; + case Direct: + break; + case SOCKSProxy: { + // FIXME: SOCKS5 doesn't do challenge-response, should we try to apply credentials from Keychain right away? + // But SOCKS5 credentials don't work at the time of this writing anyway, see <rdar://6776698>. + const void* proxyKeys[] = { kCFStreamPropertySOCKSProxyHost, kCFStreamPropertySOCKSProxyPort }; + const void* proxyValues[] = { m_proxyHost.get(), m_proxyPort.get() }; + RetainPtr<CFDictionaryRef> connectDictionary(AdoptCF, CFDictionaryCreate(0, proxyKeys, proxyValues, WTF_ARRAY_LENGTH(proxyKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + CFReadStreamSetProperty(m_readStream.get(), kCFStreamPropertySOCKSProxy, connectDictionary.get()); + break; + } + case CONNECTProxy: + wkSetCONNECTProxyForStream(m_readStream.get(), m_proxyHost.get(), m_proxyPort.get()); + break; + } + + if (shouldUseSSL()) { + const void* keys[] = { kCFStreamSSLPeerName, kCFStreamSSLLevel }; + const void* values[] = { host.get(), kCFStreamSocketSecurityLevelNegotiatedSSL }; + RetainPtr<CFDictionaryRef> settings(AdoptCF, CFDictionaryCreate(0, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + CFReadStreamSetProperty(m_readStream.get(), kCFStreamPropertySSLSettings, settings.get()); + CFWriteStreamSetProperty(m_writeStream.get(), kCFStreamPropertySSLSettings, settings.get()); + } +} + +static bool getStoredCONNECTProxyCredentials(const ProtectionSpace& protectionSpace, String& login, String& password) +{ + // Try system credential storage first, matching HTTP behavior (CFNetwork only asks the client for password if it couldn't find it in Keychain). + Credential storedCredential = CredentialStorage::getFromPersistentStorage(protectionSpace); + if (storedCredential.isEmpty()) + storedCredential = CredentialStorage::get(protectionSpace); + + if (storedCredential.isEmpty()) + return false; + + login = storedCredential.user(); + password = storedCredential.password(); + + return true; +} + +static ProtectionSpaceAuthenticationScheme authenticationSchemeFromAuthenticationMethod(CFStringRef method) +{ + if (CFEqual(method, kCFHTTPAuthenticationSchemeBasic)) + return ProtectionSpaceAuthenticationSchemeHTTPBasic; + if (CFEqual(method, kCFHTTPAuthenticationSchemeDigest)) + return ProtectionSpaceAuthenticationSchemeHTTPDigest; +#ifndef BUILDING_ON_TIGER + if (CFEqual(method, kCFHTTPAuthenticationSchemeNTLM)) + return ProtectionSpaceAuthenticationSchemeNTLM; + if (CFEqual(method, kCFHTTPAuthenticationSchemeNegotiate)) + return ProtectionSpaceAuthenticationSchemeNegotiate; +#endif + ASSERT_NOT_REACHED(); + return ProtectionSpaceAuthenticationSchemeUnknown; +} + +void SocketStreamHandle::addCONNECTCredentials(CFHTTPMessageRef proxyResponse) +{ + RetainPtr<CFHTTPAuthenticationRef> authentication(AdoptCF, CFHTTPAuthenticationCreateFromResponse(0, proxyResponse)); + + if (!CFHTTPAuthenticationRequiresUserNameAndPassword(authentication.get())) { + // That's all we can offer... + m_client->didFail(this, SocketStreamError()); // FIXME: Provide a sensible error. + return; + } + + int port = 0; + CFNumberGetValue(m_proxyPort.get(), kCFNumberIntType, &port); + RetainPtr<CFStringRef> methodCF(AdoptCF, CFHTTPAuthenticationCopyMethod(authentication.get())); + RetainPtr<CFStringRef> realmCF(AdoptCF, CFHTTPAuthenticationCopyRealm(authentication.get())); + ProtectionSpace protectionSpace(String(m_proxyHost.get()), port, ProtectionSpaceProxyHTTPS, String(realmCF.get()), authenticationSchemeFromAuthenticationMethod(methodCF.get())); + String login; + String password; + if (!m_sentStoredCredentials && getStoredCONNECTProxyCredentials(protectionSpace, login, password)) { + // Try to apply stored credentials, if we haven't tried those already. + RetainPtr<CFStringRef> loginCF(AdoptCF, login.createCFString()); + RetainPtr<CFStringRef> passwordCF(AdoptCF, password.createCFString()); + // Creating a temporary request to make CFNetwork apply credentials to it. Unfortunately, this cannot work with NTLM authentication. + RetainPtr<CFHTTPMessageRef> dummyRequest(AdoptCF, CFHTTPMessageCreateRequest(0, CFSTR("GET"), m_httpsURL.get(), kCFHTTPVersion1_1)); + + Boolean appliedCredentials = CFHTTPMessageApplyCredentials(dummyRequest.get(), authentication.get(), loginCF.get(), passwordCF.get(), 0); + ASSERT_UNUSED(appliedCredentials, appliedCredentials); + + RetainPtr<CFStringRef> proxyAuthorizationString(AdoptCF, CFHTTPMessageCopyHeaderFieldValue(dummyRequest.get(), CFSTR("Proxy-Authorization"))); + + if (!proxyAuthorizationString) { + // Fails e.g. for NTLM auth. + m_client->didFail(this, SocketStreamError()); // FIXME: Provide a sensible error. + return; + } + + // Setting the authorization results in a new connection attempt. + wkSetCONNECTProxyAuthorizationForStream(m_readStream.get(), proxyAuthorizationString.get()); + m_sentStoredCredentials = true; + return; + } + + // FIXME: Ask the client if credentials could not be found. + + m_client->didFail(this, SocketStreamError()); // FIXME: Provide a sensible error. +} + +CFStringRef SocketStreamHandle::copyCFStreamDescription(void* info) +{ + SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(info); + return ("WebKit socket stream, " + handle->m_url.string()).createCFString(); +} + +struct MainThreadEventCallbackInfo { + MainThreadEventCallbackInfo(CFStreamEventType type, SocketStreamHandle* handle) : type(type), handle(handle) { } + CFStreamEventType type; + SocketStreamHandle* handle; +}; + +void SocketStreamHandle::readStreamCallback(CFReadStreamRef stream, CFStreamEventType type, void* clientCallBackInfo) +{ + SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(clientCallBackInfo); + ASSERT_UNUSED(stream, stream == handle->m_readStream.get()); +#if PLATFORM(WIN) + MainThreadEventCallbackInfo info(type, handle); + callOnMainThreadAndWait(readStreamCallbackMainThread, &info); +#else + ASSERT(isMainThread()); + handle->readStreamCallback(type); +#endif +} + +void SocketStreamHandle::writeStreamCallback(CFWriteStreamRef stream, CFStreamEventType type, void* clientCallBackInfo) +{ + SocketStreamHandle* handle = static_cast<SocketStreamHandle*>(clientCallBackInfo); + ASSERT_UNUSED(stream, stream == handle->m_writeStream.get()); +#if PLATFORM(WIN) + MainThreadEventCallbackInfo info(type, handle); + callOnMainThreadAndWait(writeStreamCallbackMainThread, &info); +#else + ASSERT(isMainThread()); + handle->writeStreamCallback(type); +#endif +} + +#if PLATFORM(WIN) +void SocketStreamHandle::readStreamCallbackMainThread(void* invocation) +{ + MainThreadEventCallbackInfo* info = static_cast<MainThreadEventCallbackInfo*>(invocation); + info->handle->readStreamCallback(info->type); +} + +void SocketStreamHandle::writeStreamCallbackMainThread(void* invocation) +{ + MainThreadEventCallbackInfo* info = static_cast<MainThreadEventCallbackInfo*>(invocation); + info->handle->writeStreamCallback(info->type); +} +#endif // PLATFORM(WIN) + +void SocketStreamHandle::readStreamCallback(CFStreamEventType type) +{ + switch(type) { + case kCFStreamEventNone: + break; + case kCFStreamEventOpenCompleted: + break; + case kCFStreamEventHasBytesAvailable: { + if (m_connectingSubstate == WaitingForConnect) { + if (m_connectionType == CONNECTProxy) { + RetainPtr<CFHTTPMessageRef> proxyResponse(AdoptCF, wkCopyCONNECTProxyResponse(m_readStream.get(), m_httpsURL.get())); + if (proxyResponse && (407 == CFHTTPMessageGetResponseStatusCode(proxyResponse.get()))) { + addCONNECTCredentials(proxyResponse.get()); + return; + } + } + } else if (m_connectingSubstate == WaitingForCredentials) + break; + + if (m_connectingSubstate == WaitingForConnect) { + m_connectingSubstate = Connected; + m_state = Open; + + RefPtr<SocketStreamHandle> protect(this); // The client can close the handle, potentially removing the last reference. + m_client->didOpen(this); + if (m_state == Closed) + break; + // Fall through. + } else if (m_state == Closed) + break; + + ASSERT(m_state == Open); + ASSERT(m_connectingSubstate == Connected); + + CFIndex length; + UInt8 localBuffer[1024]; // Used if CFReadStreamGetBuffer couldn't return anything. + const UInt8* ptr = CFReadStreamGetBuffer(m_readStream.get(), 0, &length); + if (!ptr) { + length = CFReadStreamRead(m_readStream.get(), localBuffer, sizeof(localBuffer)); + ptr = localBuffer; + } + + m_client->didReceiveData(this, reinterpret_cast<const char*>(ptr), length); + + break; + } + case kCFStreamEventCanAcceptBytes: + ASSERT_NOT_REACHED(); + break; + case kCFStreamEventErrorOccurred: { +#ifndef BUILDING_ON_TIGER + RetainPtr<CFErrorRef> error(AdoptCF, CFReadStreamCopyError(m_readStream.get())); + reportErrorToClient(error.get()); +#else + CFStreamError error = CFReadStreamGetError(m_readStream.get()); + m_client->didFail(this, SocketStreamError(error.error)); // FIXME: Provide a sensible error. +#endif + break; + } + case kCFStreamEventEndEncountered: + platformClose(); + break; + } +} + +void SocketStreamHandle::writeStreamCallback(CFStreamEventType type) +{ + switch(type) { + case kCFStreamEventNone: + break; + case kCFStreamEventOpenCompleted: + break; + case kCFStreamEventHasBytesAvailable: + ASSERT_NOT_REACHED(); + break; + case kCFStreamEventCanAcceptBytes: { + // Possibly, a spurious event from CONNECT handshake. + if (!CFWriteStreamCanAcceptBytes(m_writeStream.get())) + return; + + if (m_connectingSubstate == WaitingForCredentials) + break; + + if (m_connectingSubstate == WaitingForConnect) { + m_connectingSubstate = Connected; + m_state = Open; + + RefPtr<SocketStreamHandle> protect(this); // The client can close the handle, potentially removing the last reference. + m_client->didOpen(this); + break; + } + + ASSERT(m_state == Open); + ASSERT(m_connectingSubstate == Connected); + + sendPendingData(); + break; + } + case kCFStreamEventErrorOccurred: { +#ifndef BUILDING_ON_TIGER + RetainPtr<CFErrorRef> error(AdoptCF, CFWriteStreamCopyError(m_writeStream.get())); + reportErrorToClient(error.get()); +#else + CFStreamError error = CFWriteStreamGetError(m_writeStream.get()); + m_client->didFail(this, SocketStreamError(error.error)); // FIXME: Provide a sensible error. +#endif + break; + } + case kCFStreamEventEndEncountered: + // FIXME: Currently, we handle closing in read callback, but these can come independently (e.g. a server can stop listening, but keep sending data). + break; + } +} + +#ifndef BUILDING_ON_TIGER +void SocketStreamHandle::reportErrorToClient(CFErrorRef error) +{ + CFIndex errorCode = CFErrorGetCode(error); + String description; + +#if PLATFORM(MAC) + if (CFEqual(CFErrorGetDomain(error), kCFErrorDomainOSStatus)) { + const char* descriptionOSStatus = GetMacOSStatusCommentString(static_cast<OSStatus>(errorCode)); + if (descriptionOSStatus && descriptionOSStatus[0] != '\0') + description = makeString("OSStatus Error ", String::number(errorCode), ": ", descriptionOSStatus); + } +#endif + + if (description.isNull()) { + RetainPtr<CFStringRef> descriptionCF(AdoptCF, CFErrorCopyDescription(error)); + description = String(descriptionCF.get()); + } + + m_client->didFail(this, SocketStreamError(static_cast<int>(errorCode), m_url.string(), description)); +} +#endif // BUILDING_ON_TIGER + +SocketStreamHandle::~SocketStreamHandle() +{ + LOG(Network, "SocketStreamHandle %p dtor", this); + +#ifndef BUILDING_ON_TIGER + ASSERT(!m_pacRunLoopSource); +#endif +} + +int SocketStreamHandle::platformSend(const char* data, int length) +{ + if (!CFWriteStreamCanAcceptBytes(m_writeStream.get())) + return 0; + + return CFWriteStreamWrite(m_writeStream.get(), reinterpret_cast<const UInt8*>(data), length); +} + +void SocketStreamHandle::platformClose() +{ + LOG(Network, "SocketStreamHandle %p platformClose", this); + +#ifndef BUILDING_ON_TIGER + if (m_pacRunLoopSource) + removePACRunLoopSource(); +#endif + + ASSERT(!m_readStream == !m_writeStream); + if (!m_readStream) + return; + + CFReadStreamUnscheduleFromRunLoop(m_readStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + CFWriteStreamUnscheduleFromRunLoop(m_writeStream.get(), CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + + CFReadStreamClose(m_readStream.get()); + CFWriteStreamClose(m_writeStream.get()); + + m_readStream = 0; + m_writeStream = 0; + + m_client->didClose(this); +} + +void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&) +{ +} + +void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) +{ +} + +void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&) +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/AuthenticationChallenge.h b/Source/WebCore/platform/network/chromium/AuthenticationChallenge.h new file mode 100644 index 0000000..e2d1f42 --- /dev/null +++ b/Source/WebCore/platform/network/chromium/AuthenticationChallenge.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 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 COMPUTER, 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 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 AuthenticationChallenge_h +#define AuthenticationChallenge_h + +#include "AuthenticationChallengeBase.h" +#include "AuthenticationClient.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + + class AuthenticationChallenge : public AuthenticationChallengeBase { + public: + AuthenticationChallenge() {} + AuthenticationChallenge(const ProtectionSpace&, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse&, const ResourceError&); + + AuthenticationClient* authenticationClient() const { return m_authenticationClient.get(); } + + private: + friend class AuthenticationChallengeBase; + static bool platformCompare(const AuthenticationChallenge&, const AuthenticationChallenge&); + + RefPtr<AuthenticationClient> m_authenticationClient; + }; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/chromium/AuthenticationChallengeChromium.cpp b/Source/WebCore/platform/network/chromium/AuthenticationChallengeChromium.cpp new file mode 100644 index 0000000..3c13b39 --- /dev/null +++ b/Source/WebCore/platform/network/chromium/AuthenticationChallengeChromium.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2008, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "AuthenticationChallenge.h" + +namespace WebCore { + +bool AuthenticationChallenge::platformCompare(const AuthenticationChallenge&, const AuthenticationChallenge&) +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/CookieJarChromium.cpp b/Source/WebCore/platform/network/chromium/CookieJarChromium.cpp new file mode 100644 index 0000000..e17816a --- /dev/null +++ b/Source/WebCore/platform/network/chromium/CookieJarChromium.cpp @@ -0,0 +1,70 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "CookieJar.h" + +#include "Cookie.h" +#include "ChromiumBridge.h" +#include "Document.h" + +namespace WebCore { + +void setCookies(Document* document, const KURL& url, const String& value) +{ + ChromiumBridge::setCookies(document, url, value); +} + +String cookies(const Document* document, const KURL& url) +{ + return ChromiumBridge::cookies(document, url); +} + +String cookieRequestHeaderFieldValue(const Document* document, const KURL& url) +{ + return ChromiumBridge::cookieRequestHeaderFieldValue(document, url); +} + +bool cookiesEnabled(const Document* document) +{ + return ChromiumBridge::cookiesEnabled(document); +} + +bool getRawCookies(const Document* document, const KURL& url, Vector<Cookie>& rawCookies) +{ + return ChromiumBridge::rawCookies(document, url, rawCookies); +} + +void deleteCookie(const Document* document, const KURL& url, const String& cookieName) +{ + return ChromiumBridge::deleteCookie(document, url, cookieName); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/DNSChromium.cpp b/Source/WebCore/platform/network/chromium/DNSChromium.cpp new file mode 100644 index 0000000..21fcd46 --- /dev/null +++ b/Source/WebCore/platform/network/chromium/DNSChromium.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Collin Jackson <collinj@webkit.org> + * + * 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 "DNS.h" + +#include "ChromiumBridge.h" +#include "ResourceHandle.h" + +namespace WebCore { + +void prefetchDNS(const String& hostname) +{ + ChromiumBridge::prefetchDNS(hostname); +} + +void ResourceHandle::prepareForURL(const KURL& url) +{ + return prefetchDNS(url.host()); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/NetworkStateNotifierChromium.cpp b/Source/WebCore/platform/network/chromium/NetworkStateNotifierChromium.cpp new file mode 100644 index 0000000..3b28fbd --- /dev/null +++ b/Source/WebCore/platform/network/chromium/NetworkStateNotifierChromium.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "NetworkStateNotifier.h" + +namespace WebCore { + +// Chromium doesn't currently support network state notifications. This causes +// an extra DLL to get loaded into the renderer which can slow things down a +// bit. We may want an alternate design. + +void NetworkStateNotifier::updateState() +{ +} + +NetworkStateNotifier::NetworkStateNotifier() + : m_isOnLine(true) + , m_networkStateChangedFunction(0) +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/NetworkStateNotifierPrivate.h b/Source/WebCore/platform/network/chromium/NetworkStateNotifierPrivate.h new file mode 100644 index 0000000..7628d59 --- /dev/null +++ b/Source/WebCore/platform/network/chromium/NetworkStateNotifierPrivate.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2008, 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 NetworkStateNotifierPrivate_h +#define NetworkStateNotifierPrivate_h + +namespace WebCore { + + struct NetworkStateNotifierPrivate {}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/chromium/ResourceError.h b/Source/WebCore/platform/network/chromium/ResourceError.h new file mode 100644 index 0000000..a1fbe11 --- /dev/null +++ b/Source/WebCore/platform/network/chromium/ResourceError.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 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 COMPUTER, 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 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 ResourceError_h +#define ResourceError_h + +#include "ResourceErrorBase.h" + +namespace WebCore { + + class ResourceError : public ResourceErrorBase { + public: + ResourceError() + { + } + + ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) + : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) + { + } + + private: + friend class ResourceErrorBase; + }; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/chromium/ResourceRequest.cpp b/Source/WebCore/platform/network/chromium/ResourceRequest.cpp new file mode 100644 index 0000000..69591c1 --- /dev/null +++ b/Source/WebCore/platform/network/chromium/ResourceRequest.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 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 "ResourceRequest.h" + +namespace WebCore { + +// This is used by the loader to control the number of issued parallel load requests. +unsigned initializeMaximumHTTPConnectionCountPerHost() +{ + // The chromium network stack already handles limiting the number of + // parallel requests per host, so there's no need to do it here. Therefore, + // this is set to a high value that should never be hit in practice. + 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; + data->m_hasUserGesture = m_hasUserGesture; + return data; +} + +void ResourceRequest::doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData> data) +{ + m_requestorID = data->m_requestorID; + m_requestorProcessID = data->m_requestorProcessID; + m_appCacheHostID = data->m_appCacheHostID; + m_hasUserGesture = data->m_hasUserGesture; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/ResourceRequest.h b/Source/WebCore/platform/network/chromium/ResourceRequest.h new file mode 100644 index 0000000..41ad6e0 --- /dev/null +++ b/Source/WebCore/platform/network/chromium/ResourceRequest.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * Copyright (C) 2008 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 COMPUTER, 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 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 ResourceRequest_h +#define ResourceRequest_h + +#include "ResourceRequestBase.h" + +namespace WebCore { + + class Frame; + + class ResourceRequest : public ResourceRequestBase { + public: + ResourceRequest(const String& url) + : ResourceRequestBase(KURL(ParsedURLString, url), UseProtocolCachePolicy) + , m_requestorID(0) + , m_requestorProcessID(0) + , m_appCacheHostID(0) + , m_hasUserGesture(false) + { + } + + ResourceRequest(const KURL& url) + : ResourceRequestBase(url, UseProtocolCachePolicy) + , m_requestorID(0) + , m_requestorProcessID(0) + , m_appCacheHostID(0) + , m_hasUserGesture(false) + { + } + + ResourceRequest(const KURL& url, const String& referrer, ResourceRequestCachePolicy policy = UseProtocolCachePolicy) + : ResourceRequestBase(url, policy) + , m_requestorID(0) + , m_requestorProcessID(0) + , m_appCacheHostID(0) + , m_hasUserGesture(false) + { + setHTTPReferrer(referrer); + } + + ResourceRequest() + : ResourceRequestBase(KURL(), UseProtocolCachePolicy) + , m_requestorID(0) + , m_requestorProcessID(0) + , m_appCacheHostID(0) + , m_hasUserGesture(false) + { + } + + // Allows the request to be matched up with its requestor. + int requestorID() const { return m_requestorID; } + void setRequestorID(int requestorID) { m_requestorID = requestorID; } + + // The process id of the process from which this request originated. In + // the case of out-of-process plugins, this allows to link back the + // request to the plugin process (as it is processed through a render + // view process). + int requestorProcessID() const { return m_requestorProcessID; } + void setRequestorProcessID(int requestorProcessID) { m_requestorProcessID = requestorProcessID; } + + // Allows the request to be matched up with its app cache host. + int appCacheHostID() const { return m_appCacheHostID; } + void setAppCacheHostID(int id) { m_appCacheHostID = id; } + + // True if request was user initiated. + bool hasUserGesture() const { return m_hasUserGesture; } + void setHasUserGesture(bool hasUserGesture) { m_hasUserGesture = hasUserGesture; } + + private: + friend class ResourceRequestBase; + + void doUpdatePlatformRequest() {} + void doUpdateResourceRequest() {} + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData>) const; + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>); + + int m_requestorID; + int m_requestorProcessID; + int m_appCacheHostID; + bool m_hasUserGesture; + }; + + struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { + int m_requestorID; + int m_requestorProcessID; + int m_appCacheHostID; + bool m_hasUserGesture; + }; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/chromium/ResourceResponse.cpp b/Source/WebCore/platform/network/chromium/ResourceResponse.cpp new file mode 100644 index 0000000..acd44d3 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/chromium/ResourceResponse.h b/Source/WebCore/platform/network/chromium/ResourceResponse.h new file mode 100644 index 0000000..5e99994 --- /dev/null +++ b/Source/WebCore/platform/network/chromium/ResourceResponse.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 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 COMPUTER, 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 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 ResourceResponse_h +#define ResourceResponse_h + +#include "NotImplemented.h" +#include "ResourceResponseBase.h" +#include <wtf/text/CString.h> + +namespace WebCore { + + class ResourceResponse : public ResourceResponseBase { + public: + ResourceResponse() + : m_appCacheID(0) + , m_isContentFiltered(false) + , m_isMultipartPayload(false) + , m_wasFetchedViaSPDY(false) + , m_wasNpnNegotiated(false) + , m_wasAlternateProtocolAvailable(false) + , m_wasFetchedViaProxy(false) + , m_responseTime(0) + { + } + + ResourceResponse(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) + : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename) + , m_appCacheID(0) + , m_isContentFiltered(false) + , m_isMultipartPayload(false) + , m_wasFetchedViaSPDY(false) + , m_wasNpnNegotiated(false) + , m_wasAlternateProtocolAvailable(false) + , m_wasFetchedViaProxy(false) + , m_responseTime(0) + { + } + + const CString& getSecurityInfo() const { return m_securityInfo; } + void setSecurityInfo(const CString& securityInfo) { m_securityInfo = securityInfo; } + + long long appCacheID() const { return m_appCacheID; } + void setAppCacheID(long long id) { m_appCacheID = id; } + + const KURL& appCacheManifestURL() const { return m_appCacheManifestURL; } + void setAppCacheManifestURL(const KURL& url) { m_appCacheManifestURL = url; } + + bool isContentFiltered() const { return m_isContentFiltered; } + void setIsContentFiltered(bool value) { m_isContentFiltered = value; } + + bool wasFetchedViaSPDY() const { return m_wasFetchedViaSPDY; } + void setWasFetchedViaSPDY(bool value) { m_wasFetchedViaSPDY = value; } + + bool wasNpnNegotiated() const { return m_wasNpnNegotiated; } + void setWasNpnNegotiated(bool value) { m_wasNpnNegotiated = value; } + + bool wasAlternateProtocolAvailable() const + { + return m_wasAlternateProtocolAvailable; + } + void setWasAlternateProtocolAvailable(bool value) + { + m_wasAlternateProtocolAvailable = value; + } + + bool wasFetchedViaProxy() const { return m_wasFetchedViaProxy; } + void setWasFetchedViaProxy(bool value) { m_wasFetchedViaProxy = value; } + + bool isMultipartPayload() const { return m_isMultipartPayload; } + void setIsMultipartPayload(bool value) { m_isMultipartPayload = value; } + + double responseTime() const { return m_responseTime; } + void setResponseTime(double responseTime) { m_responseTime = responseTime; } + + private: + friend class ResourceResponseBase; + + // An opaque value that contains some information regarding the security of + // the connection for this request, such as SSL connection info (empty + // string if not over HTTPS). + CString m_securityInfo; + + void doUpdateResourceResponse() + { + 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; + + // The manifest url of the appcache this response was retrieved from, if any. + // Note: only valid for main resource responses. + KURL m_appCacheManifestURL; + + // Whether the contents for this response has been altered/blocked (usually + // for security reasons. + bool m_isContentFiltered; + + // Set to true if this is part of a multipart response. + bool m_isMultipartPayload; + + // Was the resource fetched over SPDY. See http://dev.chromium.org/spdy + bool m_wasFetchedViaSPDY; + + // Was the resource fetched over a channel which used TLS/Next-Protocol-Negotiation (also SPDY related). + bool m_wasNpnNegotiated; + + // Was the resource fetched over a channel which specified "Alternate-Protocol" + // (e.g.: Alternate-Protocol: 443:npn-spdy/1). + bool m_wasAlternateProtocolAvailable; + + // Was the resource fetched over an explicit proxy (HTTP, SOCKS, etc). + bool m_wasFetchedViaProxy; + + // The time at which the response headers were received. For cached + // responses, this time could be "far" in the past. + 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/Source/WebCore/platform/network/chromium/SocketStreamError.h b/Source/WebCore/platform/network/chromium/SocketStreamError.h new file mode 100644 index 0000000..540496b --- /dev/null +++ b/Source/WebCore/platform/network/chromium/SocketStreamError.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamError_h +#define SocketStreamError_h + +#include "SocketStreamErrorBase.h" + +namespace WebCore { + +class SocketStreamError : public SocketStreamErrorBase { +public: + SocketStreamError() { } + explicit SocketStreamError(int errorCode) + : SocketStreamErrorBase(errorCode) { } +}; + +} // namespace WebCore + +#endif // SocketStreamError_h diff --git a/Source/WebCore/platform/network/chromium/SocketStreamHandle.h b/Source/WebCore/platform/network/chromium/SocketStreamHandle.h new file mode 100644 index 0000000..5a905cb --- /dev/null +++ b/Source/WebCore/platform/network/chromium/SocketStreamHandle.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamHandle_h +#define SocketStreamHandle_h + +#include "SocketStreamHandleBase.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class AuthenticationChallenge; + class Credential; + class SocketStreamHandleClient; + class SocketStreamHandleInternal; + + class SocketStreamHandle : public RefCounted<SocketStreamHandle>, public SocketStreamHandleBase { + public: + static PassRefPtr<SocketStreamHandle> create(const KURL& url, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(url, client)); } + + virtual ~SocketStreamHandle(); + + protected: + virtual int platformSend(const char* data, int length); + virtual void platformClose(); + + private: + SocketStreamHandle(const KURL&, SocketStreamHandleClient*); + + // No authentication for streams per se, but proxy may ask for credentials. + void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); + void receivedCredential(const AuthenticationChallenge&, const Credential&); + void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&); + void receivedCancellation(const AuthenticationChallenge&); + + friend class SocketStreamHandleInternal; + OwnPtr<SocketStreamHandleInternal> m_internal; + }; + +} // namespace WebCore + +#endif // SocketStreamHandle_h diff --git a/Source/WebCore/platform/network/curl/AuthenticationChallenge.h b/Source/WebCore/platform/network/curl/AuthenticationChallenge.h new file mode 100644 index 0000000..7ace096 --- /dev/null +++ b/Source/WebCore/platform/network/curl/AuthenticationChallenge.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 AuthenticationChallenge_h +#define AuthenticationChallenge_h + +#include "AuthenticationChallengeBase.h" +#include "AuthenticationClient.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +class AuthenticationChallenge : public AuthenticationChallengeBase { +public: + AuthenticationChallenge() + { + } + + AuthenticationChallenge(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error) + : AuthenticationChallengeBase(protectionSpace, proposedCredential, previousFailureCount, response, error) + { + } + + AuthenticationClient* authenticationClient() const { return m_authenticationClient.get(); } + + RefPtr<AuthenticationClient> m_authenticationClient; +}; + +} + +#endif diff --git a/Source/WebCore/platform/network/curl/CookieJarCurl.cpp b/Source/WebCore/platform/network/curl/CookieJarCurl.cpp new file mode 100644 index 0000000..36495d0 --- /dev/null +++ b/Source/WebCore/platform/network/curl/CookieJarCurl.cpp @@ -0,0 +1,71 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "CookieJar.h" + +#include "Cookie.h" +#include "Document.h" +#include "KURL.h" +#include "PlatformString.h" +#include <wtf/HashMap.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +static HashMap<String, String> cookieJar; + +void setCookies(Document* /*document*/, const KURL& url, const String& value) +{ + cookieJar.set(url.string(), value); +} + +String cookies(const Document* /*document*/, const KURL& url) +{ + return cookieJar.get(url.string()); +} + +String cookieRequestHeaderFieldValue(const Document* /*document*/, const KURL& url) +{ + // FIXME: include HttpOnly cookie. + return cookieJar.get(url.string()); +} + +bool cookiesEnabled(const Document* /*document*/) +{ + return true; +} + +bool getRawCookies(const Document*, const KURL&, Vector<Cookie>& rawCookies) +{ + // FIXME: Not yet implemented + rawCookies.clear(); + return false; // return true when implemented +} + +void deleteCookie(const Document*, const KURL&, const String&) +{ + // FIXME: Not yet implemented +} + +#if !PLATFORM(EFL) +void setCookieStoragePrivateBrowsingEnabled(bool enabled) +{ + // FIXME: Not yet implemented +} +#endif + +} diff --git a/Source/WebCore/platform/network/curl/DNSCurl.cpp b/Source/WebCore/platform/network/curl/DNSCurl.cpp new file mode 100644 index 0000000..1ffe1a0 --- /dev/null +++ b/Source/WebCore/platform/network/curl/DNSCurl.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008 Apple Computer, 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 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 "DNS.h" + +#include "NotImplemented.h" + +namespace WebCore { + +void prefetchDNS(const String& hostname) +{ + notImplemented(); +} + +} diff --git a/Source/WebCore/platform/network/curl/FormDataStreamCurl.cpp b/Source/WebCore/platform/network/curl/FormDataStreamCurl.cpp new file mode 100644 index 0000000..e47eb07 --- /dev/null +++ b/Source/WebCore/platform/network/curl/FormDataStreamCurl.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007 Alp Toker <alp.toker@collabora.co.uk> + * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2008 Collabora Ltd. + * 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 COMPUTER, 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 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 "FormDataStreamCurl.h" + +#include "FormData.h" +#include "ResourceRequest.h" +#include <wtf/text/CString.h> + +namespace WebCore { + +FormDataStream::~FormDataStream() +{ + if (m_file) + fclose(m_file); +} + +size_t FormDataStream::read(void* ptr, size_t blockSize, size_t numberOfBlocks) +{ + // Check for overflow. + if (!numberOfBlocks || blockSize > std::numeric_limits<size_t>::max() / numberOfBlocks) + return 0; + + Vector<FormDataElement> elements; + if (m_resourceHandle->firstRequest().httpBody()) + elements = m_resourceHandle->firstRequest().httpBody()->elements(); + + if (m_formDataElementIndex >= elements.size()) + return 0; + + FormDataElement element = elements[m_formDataElementIndex]; + + size_t toSend = blockSize * numberOfBlocks; + size_t sent; + + if (element.m_type == FormDataElement::encodedFile) { + if (!m_file) + m_file = fopen(element.m_filename.utf8().data(), "rb"); + + if (!m_file) { + // FIXME: show a user error? +#ifndef NDEBUG + printf("Failed while trying to open %s for upload\n", element.m_filename.utf8().data()); +#endif + return 0; + } + + sent = fread(ptr, blockSize, numberOfBlocks, m_file); + if (!blockSize && ferror(m_file)) { + // FIXME: show a user error? +#ifndef NDEBUG + printf("Failed while trying to read %s for upload\n", element.m_filename.utf8().data()); +#endif + return 0; + } + if (feof(m_file)) { + fclose(m_file); + m_file = 0; + m_formDataElementIndex++; + } + } else { + size_t elementSize = element.m_data.size() - m_formDataElementDataOffset; + sent = elementSize > toSend ? toSend : elementSize; + memcpy(ptr, element.m_data.data() + m_formDataElementDataOffset, sent); + if (elementSize > sent) + m_formDataElementDataOffset += sent; + else { + m_formDataElementDataOffset = 0; + m_formDataElementIndex++; + } + } + + return sent; +} + +bool FormDataStream::hasMoreElements() const +{ + Vector<FormDataElement> elements; + if (m_resourceHandle->firstRequest().httpBody()) + elements = m_resourceHandle->firstRequest().httpBody()->elements(); + + return m_formDataElementIndex < elements.size(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/curl/FormDataStreamCurl.h b/Source/WebCore/platform/network/curl/FormDataStreamCurl.h new file mode 100644 index 0000000..bba1547 --- /dev/null +++ b/Source/WebCore/platform/network/curl/FormDataStreamCurl.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 Apple Computer, 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 COMPUTER, 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 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 FormDataStreamCurl_h +#define FormDataStreamCurl_h + +#include "config.h" + +#include "FileSystem.h" +#include "ResourceHandle.h" +#include <stdio.h> + +namespace WebCore { + +class FormDataStream { +public: + FormDataStream(ResourceHandle* handle) + : m_resourceHandle(handle) + , m_file(0) + , m_formDataElementIndex(0) + , m_formDataElementDataOffset(0) + { + } + + ~FormDataStream(); + + size_t read(void* ptr, size_t blockSize, size_t numberOfBlocks); + bool hasMoreElements() const; + +private: + // We can hold a weak reference to our ResourceHandle as it holds a strong reference + // to us through its ResourceHandleInternal. + ResourceHandle* m_resourceHandle; + + FILE* m_file; + size_t m_formDataElementIndex; + size_t m_formDataElementDataOffset; +}; + +} // namespace WebCore + +#endif // FormDataStreamCurl_h diff --git a/Source/WebCore/platform/network/curl/ProxyServerCurl.cpp b/Source/WebCore/platform/network/curl/ProxyServerCurl.cpp new file mode 100644 index 0000000..212768a --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/curl/ResourceError.h b/Source/WebCore/platform/network/curl/ResourceError.h new file mode 100644 index 0000000..ca8d36b --- /dev/null +++ b/Source/WebCore/platform/network/curl/ResourceError.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceError_h +#define ResourceError_h + +#include "ResourceErrorBase.h" + +namespace WebCore { + +class ResourceError : public ResourceErrorBase +{ +public: + ResourceError() + { + } + + ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) + : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) + { + } +}; + +} + +#endif // ResourceError_h_ diff --git a/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp b/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp new file mode 100644 index 0000000..f360f86 --- /dev/null +++ b/Source/WebCore/platform/network/curl/ResourceHandleCurl.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005, 2006 Michael Emmel mike.emmel@gmail.com + * 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 COMPUTER, 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 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 "ResourceHandle.h" + +#include "CachedResourceLoader.h" +#include "NotImplemented.h" +#include "ResourceHandleInternal.h" +#include "ResourceHandleManager.h" +#include "SharedBuffer.h" + +#if PLATFORM(WIN) && PLATFORM(CF) +#include <wtf/PassRefPtr.h> +#include <wtf/RetainPtr.h> +#endif + +namespace WebCore { + +class WebCoreSynchronousLoader : public ResourceHandleClient { +public: + WebCoreSynchronousLoader(); + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); + virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/); + virtual void didFail(ResourceHandle*, const ResourceError&); + + ResourceResponse resourceResponse() const { return m_response; } + ResourceError resourceError() const { return m_error; } + Vector<char> data() const { return m_data; } + +private: + ResourceResponse m_response; + ResourceError m_error; + Vector<char> m_data; +}; + +WebCoreSynchronousLoader::WebCoreSynchronousLoader() +{ +} + +void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) +{ + m_response = response; +} + +void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int) +{ + m_data.append(data, length); +} + +void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*, double) +{ +} + +void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error) +{ + m_error = error; +} + +ResourceHandleInternal::~ResourceHandleInternal() +{ + fastFree(m_url); + if (m_customHeaders) + curl_slist_free_all(m_customHeaders); +} + +ResourceHandle::~ResourceHandle() +{ + cancel(); +} + +bool ResourceHandle::start(NetworkingContext* context) +{ + // The frame could be null if the ResourceHandle is not associated to any + // Frame, e.g. if we are downloading a file. + // If the frame is not null but the page is null this must be an attempted + // load from an unload handler, so let's just block it. + // If both the frame and the page are not null the context is valid. + if (context && !context->isValid()) + return false; + + ResourceHandleManager::sharedInstance()->add(this); + return true; +} + +void ResourceHandle::cancel() +{ + ResourceHandleManager::sharedInstance()->cancel(this); +} + +PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() +{ + return 0; +} + +bool ResourceHandle::supportsBufferedData() +{ + return false; +} + +#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()); +} +#endif + +#if PLATFORM(WIN) && PLATFORM(CF) +// FIXME: The CFDataRef will need to be something else when +// building without +static HashMap<String, RetainPtr<CFDataRef> >& clientCerts() +{ + static HashMap<String, RetainPtr<CFDataRef> > certs; + return certs; +} + +void ResourceHandle::setClientCertificate(const String& host, CFDataRef cert) +{ + clientCerts().set(host.lower(), cert); +} +#endif + +void ResourceHandle::platformSetDefersLoading(bool defers) +{ +#if LIBCURL_VERSION_NUM > 0x071200 + if (!d->m_handle) + return; + + if (defers) { + CURLcode error = curl_easy_pause(d->m_handle, CURLPAUSE_ALL); + // If we could not defer the handle, so don't do it. + if (error != CURLE_OK) + return; + } else { + CURLcode error = curl_easy_pause(d->m_handle, CURLPAUSE_CONT); + if (error != CURLE_OK) + // Restarting the handle has failed so just cancel it. + cancel(); + } +#else + LOG_ERROR("Deferred loading is implemented if libcURL version is above 7.18.0"); +#endif +} + +bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*) +{ + notImplemented(); + return false; +} + +bool ResourceHandle::loadsBlocked() +{ + notImplemented(); + return false; +} + +void ResourceHandle::loadResourceSynchronously(NetworkingContext*, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) +{ + WebCoreSynchronousLoader syncLoader; + RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, &syncLoader, true, false)); + + ResourceHandleManager* manager = ResourceHandleManager::sharedInstance(); + + manager->dispatchSynchronousJob(handle.get()); + + error = syncLoader.resourceError(); + data = syncLoader.data(); + response = syncLoader.resourceResponse(); +} + +//stubs needed for windows version +void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void ResourceHandle::receivedCredential(const AuthenticationChallenge&, const Credential&) +{ + notImplemented(); +} + +void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void ResourceHandle::receivedCancellation(const AuthenticationChallenge&) +{ + notImplemented(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/curl/ResourceHandleManager.cpp b/Source/WebCore/platform/network/curl/ResourceHandleManager.cpp new file mode 100644 index 0000000..2e4259c --- /dev/null +++ b/Source/WebCore/platform/network/curl/ResourceHandleManager.cpp @@ -0,0 +1,753 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2008 Nuanti Ltd. + * Copyright (C) 2009 Appcelerator Inc. + * Copyright (C) 2009 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 COMPUTER, 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 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 "ResourceHandleManager.h" + +#include "DataURL.h" +#include "HTTPParsers.h" +#include "MIMETypeRegistry.h" +#include "NotImplemented.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceHandleInternal.h" + +#include <errno.h> +#include <stdio.h> +#if PLATFORM(CF) +#include <wtf/RetainPtr.h> +#endif +#include <wtf/Threading.h> +#include <wtf/Vector.h> +#include <wtf/text/CString.h> + +#if !OS(WINDOWS) +#include <sys/param.h> +#define MAX_PATH MAXPATHLEN +#endif + +namespace WebCore { + +const int selectTimeoutMS = 5; +const double pollTimeSeconds = 0.05; +const int maxRunningJobs = 5; + +static const bool ignoreSSLErrors = getenv("WEBKIT_IGNORE_SSL_ERRORS"); + +static CString certificatePath() +{ +#if PLATFORM(CF) + CFBundleRef webKitBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.WebKit")); + if (webKitBundle) { + RetainPtr<CFURLRef> certURLRef(AdoptCF, CFBundleCopyResourceURL(webKitBundle, CFSTR("cacert"), CFSTR("pem"), CFSTR("certificates"))); + if (certURLRef) { + char path[MAX_PATH]; + CFURLGetFileSystemRepresentation(certURLRef.get(), false, reinterpret_cast<UInt8*>(path), MAX_PATH); + return path; + } + } +#endif + char* envPath = getenv("CURL_CA_BUNDLE_PATH"); + if (envPath) + return envPath; + + return CString(); +} + +static Mutex* sharedResourceMutex(curl_lock_data data) { + DEFINE_STATIC_LOCAL(Mutex, cookieMutex, ()); + DEFINE_STATIC_LOCAL(Mutex, dnsMutex, ()); + DEFINE_STATIC_LOCAL(Mutex, shareMutex, ()); + + switch (data) { + case CURL_LOCK_DATA_COOKIE: + return &cookieMutex; + case CURL_LOCK_DATA_DNS: + return &dnsMutex; + case CURL_LOCK_DATA_SHARE: + return &shareMutex; + default: + ASSERT_NOT_REACHED(); + return NULL; + } +} + +// libcurl does not implement its own thread synchronization primitives. +// these two functions provide mutexes for cookies, and for the global DNS +// cache. +static void curl_lock_callback(CURL* handle, curl_lock_data data, curl_lock_access access, void* userPtr) +{ + if (Mutex* mutex = sharedResourceMutex(data)) + mutex->lock(); +} + +static void curl_unlock_callback(CURL* handle, curl_lock_data data, void* userPtr) +{ + if (Mutex* mutex = sharedResourceMutex(data)) + mutex->unlock(); +} + +ResourceHandleManager::ResourceHandleManager() + : m_downloadTimer(this, &ResourceHandleManager::downloadTimerCallback) + , m_cookieJarFileName(0) + , m_certificatePath (certificatePath()) + , m_runningJobs(0) + +{ + curl_global_init(CURL_GLOBAL_ALL); + m_curlMultiHandle = curl_multi_init(); + m_curlShareHandle = curl_share_init(); + curl_share_setopt(m_curlShareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); + curl_share_setopt(m_curlShareHandle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); + curl_share_setopt(m_curlShareHandle, CURLSHOPT_LOCKFUNC, curl_lock_callback); + curl_share_setopt(m_curlShareHandle, CURLSHOPT_UNLOCKFUNC, curl_unlock_callback); +} + +ResourceHandleManager::~ResourceHandleManager() +{ + curl_multi_cleanup(m_curlMultiHandle); + curl_share_cleanup(m_curlShareHandle); + if (m_cookieJarFileName) + fastFree(m_cookieJarFileName); + curl_global_cleanup(); +} + +void ResourceHandleManager::setCookieJarFileName(const char* cookieJarFileName) +{ + m_cookieJarFileName = fastStrDup(cookieJarFileName); +} + +ResourceHandleManager* ResourceHandleManager::sharedInstance() +{ + static ResourceHandleManager* sharedInstance = 0; + if (!sharedInstance) + sharedInstance = new ResourceHandleManager(); + return sharedInstance; +} + +static void handleLocalReceiveResponse (CURL* handle, ResourceHandle* job, ResourceHandleInternal* d) +{ + // since the code in headerCallback will not have run for local files + // the code to set the URL and fire didReceiveResponse is never run, + // which means the ResourceLoader's response does not contain the URL. + // Run the code here for local files to resolve the issue. + // 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_UNUSED(err, CURLE_OK == err); + d->m_response.setURL(KURL(ParsedURLString, hdr)); + if (d->client()) + d->client()->didReceiveResponse(job, d->m_response); + d->m_response.setResponseFired(true); +} + + +// called with data after all headers have been processed via headerCallback +static size_t writeCallback(void* ptr, size_t size, size_t nmemb, void* data) +{ + ResourceHandle* job = static_cast<ResourceHandle*>(data); + ResourceHandleInternal* d = job->getInternal(); + if (d->m_cancelled) + return 0; + +#if LIBCURL_VERSION_NUM > 0x071200 + // We should never be called when deferred loading is activated. + ASSERT(!d->m_defersLoading); +#endif + + size_t totalSize = size * nmemb; + + // this shouldn't be necessary but apparently is. CURL writes the data + // of html page even if it is a redirect that was handled internally + // can be observed e.g. on gmail.com + CURL* h = d->m_handle; + long httpCode = 0; + CURLcode err = curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode); + if (CURLE_OK == err && httpCode >= 300 && httpCode < 400) + return totalSize; + + if (!d->m_response.responseFired()) { + handleLocalReceiveResponse(h, job, d); + if (d->m_cancelled) + return 0; + } + + if (d->client()) + d->client()->didReceiveData(job, static_cast<char*>(ptr), totalSize, 0); + return totalSize; +} + +/* + * This is being called for each HTTP header in the response. This includes '\r\n' + * for the last line of the header. + * + * We will add each HTTP Header to the ResourceResponse and on the termination + * of the header (\r\n) we will parse Content-Type and Content-Disposition and + * update the ResourceResponse and then send it away. + * + */ +static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* data) +{ + ResourceHandle* job = static_cast<ResourceHandle*>(data); + ResourceHandleInternal* d = job->getInternal(); + if (d->m_cancelled) + return 0; + +#if LIBCURL_VERSION_NUM > 0x071200 + // We should never be called when deferred loading is activated. + ASSERT(!d->m_defersLoading); +#endif + + size_t totalSize = size * nmemb; + ResourceHandleClient* client = d->client(); + + String header(static_cast<const char*>(ptr), totalSize); + + /* + * a) We can finish and send the ResourceResponse + * b) We will add the current header to the HTTPHeaderMap of the ResourceResponse + * + * The HTTP standard requires to use \r\n but for compatibility it recommends to + * accept also \n. + */ + if (header == String("\r\n") || header == String("\n")) { + CURL* h = d->m_handle; + CURLcode err; + + double contentLength = 0; + err = curl_easy_getinfo(h, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &contentLength); + d->m_response.setExpectedContentLength(static_cast<long long int>(contentLength)); + + const char* hdr; + err = curl_easy_getinfo(h, CURLINFO_EFFECTIVE_URL, &hdr); + d->m_response.setURL(KURL(ParsedURLString, hdr)); + + long httpCode = 0; + err = curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode); + d->m_response.setHTTPStatusCode(httpCode); + + d->m_response.setMimeType(extractMIMETypeFromMediaType(d->m_response.httpHeaderField("Content-Type"))); + d->m_response.setTextEncodingName(extractCharsetFromMediaType(d->m_response.httpHeaderField("Content-Type"))); + d->m_response.setSuggestedFilename(filenameFromHTTPContentDisposition(d->m_response.httpHeaderField("Content-Disposition"))); + + // HTTP redirection + if (httpCode >= 300 && httpCode < 400) { + String location = d->m_response.httpHeaderField("location"); + if (!location.isEmpty()) { + KURL newURL = KURL(job->firstRequest().url(), location); + + ResourceRequest redirectedRequest = job->firstRequest(); + redirectedRequest.setURL(newURL); + if (client) + client->willSendRequest(job, redirectedRequest, d->m_response); + + d->m_firstRequest.setURL(newURL); + + return totalSize; + } + } + + if (client) + client->didReceiveResponse(job, d->m_response); + d->m_response.setResponseFired(true); + + } else { + int splitPos = header.find(":"); + if (splitPos != -1) + d->m_response.setHTTPHeaderField(header.left(splitPos), header.substring(splitPos+1).stripWhiteSpace()); + } + + return totalSize; +} + +/* This is called to obtain HTTP POST or PUT data. + Iterate through FormData elements and upload files. + Carefully respect the given buffer size and fill the rest of the data at the next calls. +*/ +size_t readCallback(void* ptr, size_t size, size_t nmemb, void* data) +{ + ResourceHandle* job = static_cast<ResourceHandle*>(data); + ResourceHandleInternal* d = job->getInternal(); + + if (d->m_cancelled) + return 0; + +#if LIBCURL_VERSION_NUM > 0x071200 + // We should never be called when deferred loading is activated. + ASSERT(!d->m_defersLoading); +#endif + + if (!size || !nmemb) + return 0; + + if (!d->m_formDataStream.hasMoreElements()) + return 0; + + size_t sent = d->m_formDataStream.read(ptr, size, nmemb); + + // Something went wrong so cancel the job. + if (!sent) + job->cancel(); + + return sent; +} + +void ResourceHandleManager::downloadTimerCallback(Timer<ResourceHandleManager>* timer) +{ + startScheduledJobs(); + + fd_set fdread; + fd_set fdwrite; + fd_set fdexcep; + int maxfd = 0; + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = selectTimeoutMS * 1000; // select waits microseconds + + // Retry 'select' if it was interrupted by a process signal. + int rc = 0; + do { + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + curl_multi_fdset(m_curlMultiHandle, &fdread, &fdwrite, &fdexcep, &maxfd); + // When the 3 file descriptors are empty, winsock will return -1 + // and bail out, stopping the file download. So make sure we + // have valid file descriptors before calling select. + if (maxfd >= 0) + rc = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); + } while (rc == -1 && errno == EINTR); + + if (-1 == rc) { +#ifndef NDEBUG + perror("bad: select() returned -1: "); +#endif + return; + } + + int runningHandles = 0; + while (curl_multi_perform(m_curlMultiHandle, &runningHandles) == CURLM_CALL_MULTI_PERFORM) { } + + // check the curl messages indicating completed transfers + // and free their resources + while (true) { + int messagesInQueue; + CURLMsg* msg = curl_multi_info_read(m_curlMultiHandle, &messagesInQueue); + if (!msg) + break; + + // find the node which has same d->m_handle as completed transfer + CURL* handle = msg->easy_handle; + ASSERT(handle); + ResourceHandle* job = 0; + CURLcode err = curl_easy_getinfo(handle, CURLINFO_PRIVATE, &job); + ASSERT_UNUSED(err, CURLE_OK == err); + ASSERT(job); + if (!job) + continue; + ResourceHandleInternal* d = job->getInternal(); + ASSERT(d->m_handle == handle); + + if (d->m_cancelled) { + removeFromCurl(job); + continue; + } + + if (CURLMSG_DONE != msg->msg) + continue; + + if (CURLE_OK == msg->data.result) { + if (!d->m_response.responseFired()) { + handleLocalReceiveResponse(d->m_handle, job, d); + if (d->m_cancelled) { + removeFromCurl(job); + continue; + } + } + + if (d->client()) + d->client()->didFinishLoading(job, 0); + } else { + char* url = 0; + curl_easy_getinfo(d->m_handle, CURLINFO_EFFECTIVE_URL, &url); +#ifndef NDEBUG + fprintf(stderr, "Curl ERROR for url='%s', error: '%s'\n", url, curl_easy_strerror(msg->data.result)); +#endif + if (d->client()) + d->client()->didFail(job, ResourceError(String(), msg->data.result, String(url), String(curl_easy_strerror(msg->data.result)))); + } + + removeFromCurl(job); + } + + bool started = startScheduledJobs(); // new jobs might have been added in the meantime + + if (!m_downloadTimer.isActive() && (started || (runningHandles > 0))) + m_downloadTimer.startOneShot(pollTimeSeconds); +} + +void ResourceHandleManager::setProxyInfo(const String& host, + unsigned long port, + ProxyType type, + const String& username, + const String& password) +{ + m_proxyType = type; + + if (!host.length()) { + m_proxy = String(""); + } else { + String userPass; + if (username.length() || password.length()) + userPass = username + ":" + password + "@"; + + m_proxy = String("http://") + userPass + host + ":" + String::number(port); + } +} + +void ResourceHandleManager::removeFromCurl(ResourceHandle* job) +{ + ResourceHandleInternal* d = job->getInternal(); + ASSERT(d->m_handle); + if (!d->m_handle) + return; + m_runningJobs--; + curl_multi_remove_handle(m_curlMultiHandle, d->m_handle); + curl_easy_cleanup(d->m_handle); + d->m_handle = 0; + job->deref(); +} + +void ResourceHandleManager::setupPUT(ResourceHandle*, struct curl_slist**) +{ + notImplemented(); +} + +/* Calculate the length of the POST. + Force chunked data transfer if size of files can't be obtained. + */ +void ResourceHandleManager::setupPOST(ResourceHandle* job, struct curl_slist** headers) +{ + ResourceHandleInternal* d = job->getInternal(); + curl_easy_setopt(d->m_handle, CURLOPT_POST, TRUE); + curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, 0); + + if (!job->firstRequest().httpBody()) + return; + + Vector<FormDataElement> elements = job->firstRequest().httpBody()->elements(); + size_t numElements = elements.size(); + if (!numElements) + return; + + // Do not stream for simple POST data + if (numElements == 1) { + job->firstRequest().httpBody()->flatten(d->m_postBytes); + if (d->m_postBytes.size() != 0) { + curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE, d->m_postBytes.size()); + curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDS, d->m_postBytes.data()); + } + return; + } + + // Obtain the total size of the POST + // The size of a curl_off_t could be different in WebKit and in cURL depending on + // compilation flags of both. For CURLOPT_POSTFIELDSIZE_LARGE we have to pass the + // right size or random data will be used as the size. + static int expectedSizeOfCurlOffT = 0; + if (!expectedSizeOfCurlOffT) { + curl_version_info_data *infoData = curl_version_info(CURLVERSION_NOW); + if (infoData->features & CURL_VERSION_LARGEFILE) + expectedSizeOfCurlOffT = sizeof(long long); + else + expectedSizeOfCurlOffT = sizeof(int); + } + +#if COMPILER(MSVC) + // work around compiler error in Visual Studio 2005. It can't properly + // handle math with 64-bit constant declarations. +#pragma warning(disable: 4307) +#endif + static const long long maxCurlOffT = (1LL << (expectedSizeOfCurlOffT * 8 - 1)) - 1; + curl_off_t size = 0; + bool chunkedTransfer = false; + for (size_t i = 0; i < numElements; i++) { + FormDataElement element = elements[i]; + if (element.m_type == FormDataElement::encodedFile) { + long long fileSizeResult; + if (getFileSize(element.m_filename, fileSizeResult)) { + if (fileSizeResult > maxCurlOffT) { + // File size is too big for specifying it to cURL + chunkedTransfer = true; + break; + } + size += fileSizeResult; + } else { + chunkedTransfer = true; + break; + } + } else + size += elements[i].m_data.size(); + } + + // cURL guesses that we want chunked encoding as long as we specify the header + if (chunkedTransfer) + *headers = curl_slist_append(*headers, "Transfer-Encoding: chunked"); + else { + if (sizeof(long long) == expectedSizeOfCurlOffT) + curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE_LARGE, (long long)size); + else + curl_easy_setopt(d->m_handle, CURLOPT_POSTFIELDSIZE_LARGE, (int)size); + } + + curl_easy_setopt(d->m_handle, CURLOPT_READFUNCTION, readCallback); + curl_easy_setopt(d->m_handle, CURLOPT_READDATA, job); +} + +void ResourceHandleManager::add(ResourceHandle* job) +{ + // we can be called from within curl, so to avoid re-entrancy issues + // schedule this job to be added the next time we enter curl download loop + job->ref(); + m_resourceHandleList.append(job); + if (!m_downloadTimer.isActive()) + m_downloadTimer.startOneShot(pollTimeSeconds); +} + +bool ResourceHandleManager::removeScheduledJob(ResourceHandle* job) +{ + int size = m_resourceHandleList.size(); + for (int i = 0; i < size; i++) { + if (job == m_resourceHandleList[i]) { + m_resourceHandleList.remove(i); + job->deref(); + return true; + } + } + return false; +} + +bool ResourceHandleManager::startScheduledJobs() +{ + // TODO: Create a separate stack of jobs for each domain. + + bool started = false; + while (!m_resourceHandleList.isEmpty() && m_runningJobs < maxRunningJobs) { + ResourceHandle* job = m_resourceHandleList[0]; + m_resourceHandleList.remove(0); + startJob(job); + started = true; + } + return started; +} + +void ResourceHandleManager::dispatchSynchronousJob(ResourceHandle* job) +{ + KURL kurl = job->firstRequest().url(); + + if (kurl.protocolIsData()) { + handleDataURL(job); + return; + } + + ResourceHandleInternal* handle = job->getInternal(); + +#if LIBCURL_VERSION_NUM > 0x071200 + // If defersLoading is true and we call curl_easy_perform + // on a paused handle, libcURL would do the transfert anyway + // and we would assert so force defersLoading to be false. + handle->m_defersLoading = false; +#endif + + initializeHandle(job); + + // curl_easy_perform blocks until the transfert is finished. + CURLcode ret = curl_easy_perform(handle->m_handle); + + if (ret != 0) { + ResourceError error(String(handle->m_url), ret, String(handle->m_url), String(curl_easy_strerror(ret))); + handle->client()->didFail(job, error); + } + + curl_easy_cleanup(handle->m_handle); +} + +void ResourceHandleManager::startJob(ResourceHandle* job) +{ + KURL kurl = job->firstRequest().url(); + + if (kurl.protocolIsData()) { + handleDataURL(job); + return; + } + + initializeHandle(job); + + m_runningJobs++; + CURLMcode ret = curl_multi_add_handle(m_curlMultiHandle, job->getInternal()->m_handle); + // don't call perform, because events must be async + // timeout will occur and do curl_multi_perform + if (ret && ret != CURLM_CALL_MULTI_PERFORM) { +#ifndef NDEBUG + fprintf(stderr, "Error %d starting job %s\n", ret, encodeWithURLEscapeSequences(job->firstRequest().url().string()).latin1().data()); +#endif + job->cancel(); + return; + } +} + +void ResourceHandleManager::initializeHandle(ResourceHandle* job) +{ + KURL kurl = job->firstRequest().url(); + + // Remove any fragment part, otherwise curl will send it as part of the request. + kurl.removeFragmentIdentifier(); + + ResourceHandleInternal* d = job->getInternal(); + String url = kurl.string(); + + if (kurl.isLocalFile()) { + String query = kurl.query(); + // Remove any query part sent to a local file. + if (!query.isEmpty()) { + int queryIndex = url.find(query); + if (queryIndex != -1) + url = url.left(queryIndex - 1); + } + // Determine the MIME type based on the path. + d->m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(url)); + } + + d->m_handle = curl_easy_init(); + +#if LIBCURL_VERSION_NUM > 0x071200 + if (d->m_defersLoading) { + 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_UNUSED(error, error == CURLE_OK); + } +#endif +#ifndef NDEBUG + if (getenv("DEBUG_CURL")) + curl_easy_setopt(d->m_handle, CURLOPT_VERBOSE, 1); +#endif + curl_easy_setopt(d->m_handle, CURLOPT_PRIVATE, job); + curl_easy_setopt(d->m_handle, CURLOPT_ERRORBUFFER, m_curlErrorBuffer); + curl_easy_setopt(d->m_handle, CURLOPT_WRITEFUNCTION, writeCallback); + curl_easy_setopt(d->m_handle, CURLOPT_WRITEDATA, job); + curl_easy_setopt(d->m_handle, CURLOPT_HEADERFUNCTION, headerCallback); + curl_easy_setopt(d->m_handle, CURLOPT_WRITEHEADER, job); + curl_easy_setopt(d->m_handle, CURLOPT_AUTOREFERER, 1); + curl_easy_setopt(d->m_handle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(d->m_handle, CURLOPT_MAXREDIRS, 10); + curl_easy_setopt(d->m_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + curl_easy_setopt(d->m_handle, CURLOPT_SHARE, m_curlShareHandle); + curl_easy_setopt(d->m_handle, CURLOPT_DNS_CACHE_TIMEOUT, 60 * 5); // 5 minutes + // FIXME: Enable SSL verification when we have a way of shipping certs + // and/or reporting SSL errors to the user. + if (ignoreSSLErrors) + curl_easy_setopt(d->m_handle, CURLOPT_SSL_VERIFYPEER, false); + + if (!m_certificatePath.isNull()) + curl_easy_setopt(d->m_handle, CURLOPT_CAINFO, m_certificatePath.data()); + + // enable gzip and deflate through Accept-Encoding: + curl_easy_setopt(d->m_handle, CURLOPT_ENCODING, ""); + + // url must remain valid through the request + ASSERT(!d->m_url); + + // url is in ASCII so latin1() will only convert it to char* without character translation. + d->m_url = fastStrDup(url.latin1().data()); + curl_easy_setopt(d->m_handle, CURLOPT_URL, d->m_url); + + if (m_cookieJarFileName) { + curl_easy_setopt(d->m_handle, CURLOPT_COOKIEFILE, m_cookieJarFileName); + curl_easy_setopt(d->m_handle, CURLOPT_COOKIEJAR, m_cookieJarFileName); + } + + struct curl_slist* headers = 0; + if (job->firstRequest().httpHeaderFields().size() > 0) { + HTTPHeaderMap customHeaders = job->firstRequest().httpHeaderFields(); + HTTPHeaderMap::const_iterator end = customHeaders.end(); + for (HTTPHeaderMap::const_iterator it = customHeaders.begin(); it != end; ++it) { + String key = it->first; + String value = it->second; + String headerString(key); + headerString.append(": "); + headerString.append(value); + CString headerLatin1 = headerString.latin1(); + headers = curl_slist_append(headers, headerLatin1.data()); + } + } + + if ("GET" == job->firstRequest().httpMethod()) + curl_easy_setopt(d->m_handle, CURLOPT_HTTPGET, TRUE); + else if ("POST" == job->firstRequest().httpMethod()) + setupPOST(job, &headers); + else if ("PUT" == job->firstRequest().httpMethod()) + setupPUT(job, &headers); + else if ("HEAD" == job->firstRequest().httpMethod()) + curl_easy_setopt(d->m_handle, CURLOPT_NOBODY, TRUE); + + if (headers) { + curl_easy_setopt(d->m_handle, CURLOPT_HTTPHEADER, headers); + d->m_customHeaders = headers; + } + // curl CURLOPT_USERPWD expects username:password + if (d->m_user.length() || d->m_pass.length()) { + String userpass = d->m_user + ":" + d->m_pass; + curl_easy_setopt(d->m_handle, CURLOPT_USERPWD, userpass.utf8().data()); + } + + // Set proxy options if we have them. + if (m_proxy.length()) { + curl_easy_setopt(d->m_handle, CURLOPT_PROXY, m_proxy.utf8().data()); + curl_easy_setopt(d->m_handle, CURLOPT_PROXYTYPE, m_proxyType); + } +} + +void ResourceHandleManager::cancel(ResourceHandle* job) +{ + if (removeScheduledJob(job)) + return; + + ResourceHandleInternal* d = job->getInternal(); + d->m_cancelled = true; + if (!m_downloadTimer.isActive()) + m_downloadTimer.startOneShot(pollTimeSeconds); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/curl/ResourceHandleManager.h b/Source/WebCore/platform/network/curl/ResourceHandleManager.h new file mode 100644 index 0000000..ff54e73 --- /dev/null +++ b/Source/WebCore/platform/network/curl/ResourceHandleManager.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * 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 COMPUTER, 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 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 ResourceHandleManager_h +#define ResourceHandleManager_h + +#include "Frame.h" +#include "PlatformString.h" +#include "Timer.h" +#include "ResourceHandleClient.h" + +#if PLATFORM(WIN) +#include <winsock2.h> +#include <windows.h> +#endif + +#include <curl/curl.h> +#include <wtf/Vector.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +class ResourceHandleManager { +public: + enum ProxyType { + HTTP = CURLPROXY_HTTP, + Socks4 = CURLPROXY_SOCKS4, + Socks4A = CURLPROXY_SOCKS4A, + Socks5 = CURLPROXY_SOCKS5, + Socks5Hostname = CURLPROXY_SOCKS5_HOSTNAME + }; + static ResourceHandleManager* sharedInstance(); + void add(ResourceHandle*); + void cancel(ResourceHandle*); + void setCookieJarFileName(const char* cookieJarFileName); + + void dispatchSynchronousJob(ResourceHandle*); + + void setupPOST(ResourceHandle*, struct curl_slist**); + void setupPUT(ResourceHandle*, struct curl_slist**); + + void setProxyInfo(const String& host = "", + unsigned long port = 0, + ProxyType type = HTTP, + const String& username = "", + const String& password = ""); + +private: + ResourceHandleManager(); + ~ResourceHandleManager(); + void downloadTimerCallback(Timer<ResourceHandleManager>*); + void removeFromCurl(ResourceHandle*); + bool removeScheduledJob(ResourceHandle*); + void startJob(ResourceHandle*); + bool startScheduledJobs(); + + void initializeHandle(ResourceHandle*); + + Timer<ResourceHandleManager> m_downloadTimer; + CURLM* m_curlMultiHandle; + CURLSH* m_curlShareHandle; + char* m_cookieJarFileName; + char m_curlErrorBuffer[CURL_ERROR_SIZE]; + Vector<ResourceHandle*> m_resourceHandleList; + const CString m_certificatePath; + int m_runningJobs; + + String m_proxy; + ProxyType m_proxyType; +}; + +} + +#endif diff --git a/Source/WebCore/platform/network/curl/ResourceRequest.h b/Source/WebCore/platform/network/curl/ResourceRequest.h new file mode 100644 index 0000000..c88501a --- /dev/null +++ b/Source/WebCore/platform/network/curl/ResourceRequest.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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 COMPUTER, 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 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 ResourceRequest_h +#define ResourceRequest_h + +#include "ResourceRequestBase.h" + +typedef const struct _CFURLRequest* CFURLRequestRef; + +namespace WebCore { + + class ResourceRequest : public ResourceRequestBase { + public: + ResourceRequest(const String& url) + : ResourceRequestBase(KURL(ParsedURLString, url), UseProtocolCachePolicy) + { + } + + ResourceRequest(const KURL& url) + : ResourceRequestBase(url, UseProtocolCachePolicy) + { + } + + ResourceRequest(const KURL& url, const String& referrer, ResourceRequestCachePolicy policy = UseProtocolCachePolicy) + : ResourceRequestBase(url, policy) + { + setHTTPReferrer(referrer); + } + + ResourceRequest() + : ResourceRequestBase(KURL(), UseProtocolCachePolicy) + { + } + + ResourceRequest(CFURLRequestRef) + : ResourceRequestBase() + { + } + + // Needed for compatibility. + CFURLRequestRef cfURLRequest() const { return 0; } + + private: + friend class ResourceRequestBase; + + void doUpdatePlatformRequest() {} + void doUpdateResourceRequest() {} + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } + }; + + struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { + }; + +} // namespace WebCore + +#endif // ResourceRequest_h diff --git a/Source/WebCore/platform/network/curl/ResourceResponse.h b/Source/WebCore/platform/network/curl/ResourceResponse.h new file mode 100644 index 0000000..49dd56c --- /dev/null +++ b/Source/WebCore/platform/network/curl/ResourceResponse.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceResponse_h +#define ResourceResponse_h + +#include "ResourceResponseBase.h" + +typedef struct _CFURLResponse* CFURLResponseRef; + +namespace WebCore { + +class ResourceResponse : public ResourceResponseBase { +public: + ResourceResponse() + : m_responseFired(false) + { + } + + ResourceResponse(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) + : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename), + m_responseFired(false) + { + } + + void setResponseFired(bool fired) { m_responseFired = fired; } + bool responseFired() { return m_responseFired; } + + // Needed for compatibility. + 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/Source/WebCore/platform/network/curl/SocketStreamError.h b/Source/WebCore/platform/network/curl/SocketStreamError.h new file mode 100644 index 0000000..f9641ad --- /dev/null +++ b/Source/WebCore/platform/network/curl/SocketStreamError.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamError_h +#define SocketStreamError_h + +#include "SocketStreamErrorBase.h" + +namespace WebCore { + + class SocketStreamError : public SocketStreamErrorBase { + public: + SocketStreamError() { } + explicit SocketStreamError(int errorCode) + : SocketStreamErrorBase(errorCode) + { + } + + }; + +} // namespace WebCore + +#endif // SocketStreamError_h diff --git a/Source/WebCore/platform/network/curl/SocketStreamHandle.h b/Source/WebCore/platform/network/curl/SocketStreamHandle.h new file mode 100644 index 0000000..64139e5 --- /dev/null +++ b/Source/WebCore/platform/network/curl/SocketStreamHandle.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamHandle_h +#define SocketStreamHandle_h + +#include "SocketStreamHandleBase.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class AuthenticationChallenge; + class Credential; + class SocketStreamHandleClient; + + class SocketStreamHandle : public RefCounted<SocketStreamHandle>, public SocketStreamHandleBase { + public: + static PassRefPtr<SocketStreamHandle> create(const KURL& url, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(url, client)); } + + virtual ~SocketStreamHandle(); + + protected: + virtual int platformSend(const char* data, int length); + virtual void platformClose(); + + private: + SocketStreamHandle(const KURL&, SocketStreamHandleClient*); + + // No authentication for streams per se, but proxy may ask for credentials. + void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); + void receivedCredential(const AuthenticationChallenge&, const Credential&); + void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&); + void receivedCancellation(const AuthenticationChallenge&); + }; + +} // namespace WebCore + +#endif // SocketStreamHandle_h diff --git a/Source/WebCore/platform/network/curl/SocketStreamHandleCurl.cpp b/Source/WebCore/platform/network/curl/SocketStreamHandleCurl.cpp new file mode 100644 index 0000000..891e96e --- /dev/null +++ b/Source/WebCore/platform/network/curl/SocketStreamHandleCurl.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009 Brent Fulgham. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "SocketStreamHandle.h" + +#include "KURL.h" +#include "Logging.h" +#include "NotImplemented.h" +#include "SocketStreamHandleClient.h" + +namespace WebCore { + +SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient* client) + : SocketStreamHandleBase(url, client) +{ + LOG(Network, "SocketStreamHandle %p new client %p", this, m_client); + notImplemented(); +} + +SocketStreamHandle::~SocketStreamHandle() +{ + LOG(Network, "SocketStreamHandle %p delete", this); + setClient(0); + notImplemented(); +} + +int SocketStreamHandle::platformSend(const char*, int) +{ + LOG(Network, "SocketStreamHandle %p platformSend", this); + notImplemented(); + return 0; +} + +void SocketStreamHandle::platformClose() +{ + LOG(Network, "SocketStreamHandle %p platformClose", this); + notImplemented(); +} + +void SocketStreamHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&) +{ + notImplemented(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/mac/AuthenticationMac.h b/Source/WebCore/platform/network/mac/AuthenticationMac.h new file mode 100644 index 0000000..9ab268e --- /dev/null +++ b/Source/WebCore/platform/network/mac/AuthenticationMac.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 AuthenticationMac_h +#define AuthenticationMac_h + +#if !USE(CFNETWORK) + +#ifdef __OBJC__ + +@class NSURLAuthenticationChallenge; +@class NSURLCredential; +@class NSURLProtectionSpace; + +namespace WebCore { + +class AuthenticationChallenge; +class Credential; +class ProtectionSpace; + +NSURLAuthenticationChallenge *mac(const AuthenticationChallenge&); +NSURLProtectionSpace *mac(const ProtectionSpace&); +NSURLCredential *mac(const Credential&); + +AuthenticationChallenge core(NSURLAuthenticationChallenge *); +ProtectionSpace core(NSURLProtectionSpace *); +Credential core(NSURLCredential *); + +} +#endif // __OBJC__ + +#endif // !USE(CFNETWORK) + +#endif // AuthenticationMac_h diff --git a/Source/WebCore/platform/network/mac/AuthenticationMac.mm b/Source/WebCore/platform/network/mac/AuthenticationMac.mm new file mode 100644 index 0000000..efa42d9 --- /dev/null +++ b/Source/WebCore/platform/network/mac/AuthenticationMac.mm @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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. + */ +#import "config.h" +#import "AuthenticationMac.h" + +#if !USE(CFNETWORK) + +#import "AuthenticationChallenge.h" +#import "AuthenticationClient.h" +#import "Credential.h" +#import "ProtectionSpace.h" + +#import <Foundation/NSURLAuthenticationChallenge.h> +#import <Foundation/NSURLCredential.h> +#import <Foundation/NSURLProtectionSpace.h> + +using namespace WebCore; + +@interface WebCoreAuthenticationClientAsChallengeSender : NSObject <NSURLAuthenticationChallengeSender> +{ + AuthenticationClient* m_client; +} +- (id)initWithAuthenticationClient:(AuthenticationClient*)client; +- (AuthenticationClient*)client; +- (void)detachClient; +@end + +@implementation WebCoreAuthenticationClientAsChallengeSender + +- (id)initWithAuthenticationClient:(AuthenticationClient*)client +{ + self = [self init]; + if (!self) + return nil; + m_client = client; + return self; +} + +- (AuthenticationClient*)client +{ + return m_client; +} + +- (void)detachClient +{ + m_client = 0; +} + +- (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + if (m_client) + m_client->receivedCredential(core(challenge), core(credential)); +} + +- (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + if (m_client) + m_client->receivedRequestToContinueWithoutCredential(core(challenge)); +} + +- (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + if (m_client) + m_client->receivedCancellation(core(challenge)); +} + +@end + +namespace WebCore { + +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) +// There is no constant in headers, but NTLM is supported. +NSString * const NSURLAuthenticationMethodNTLM = @"NSURLAuthenticationMethodNTLM"; +#endif + + +AuthenticationChallenge::AuthenticationChallenge(const ProtectionSpace& protectionSpace, + const Credential& proposedCredential, + unsigned previousFailureCount, + const ResourceResponse& response, + const ResourceError& error) + : AuthenticationChallengeBase(protectionSpace, + proposedCredential, + previousFailureCount, + response, + error) +{ +} + +AuthenticationChallenge::AuthenticationChallenge(NSURLAuthenticationChallenge *challenge) + : AuthenticationChallengeBase(core([challenge protectionSpace]), + core([challenge proposedCredential]), + [challenge previousFailureCount], + [challenge failureResponse], + [challenge error]) + , m_sender([challenge sender]) + , m_nsChallenge(challenge) +{ +} + +void AuthenticationChallenge::setAuthenticationClient(AuthenticationClient* client) +{ + if (client) { + m_sender.adoptNS([[WebCoreAuthenticationClientAsChallengeSender alloc] initWithAuthenticationClient:client]); + m_nsChallenge.adoptNS([[NSURLAuthenticationChallenge alloc] initWithAuthenticationChallenge:m_nsChallenge.get() sender:m_sender.get()]); + } else { + if ([m_sender.get() isMemberOfClass:[WebCoreAuthenticationClientAsChallengeSender class]]) + [(WebCoreAuthenticationClientAsChallengeSender *)m_sender.get() detachClient]; + } +} + +AuthenticationClient* AuthenticationChallenge::authenticationClient() const +{ + if ([m_sender.get() isMemberOfClass:[WebCoreAuthenticationClientAsChallengeSender class]]) + return [static_cast<WebCoreAuthenticationClientAsChallengeSender*>(m_sender.get()) client]; + + return 0; +} + +bool AuthenticationChallenge::platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b) +{ + if (a.sender() != b.sender()) + return false; + + if (a.nsURLAuthenticationChallenge() != b.nsURLAuthenticationChallenge()) + return false; + + return true; +} + +NSURLAuthenticationChallenge *mac(const AuthenticationChallenge& coreChallenge) +{ + if (coreChallenge.nsURLAuthenticationChallenge()) + return coreChallenge.nsURLAuthenticationChallenge(); + + return [[[NSURLAuthenticationChallenge alloc] initWithProtectionSpace:mac(coreChallenge.protectionSpace()) + proposedCredential:mac(coreChallenge.proposedCredential()) + previousFailureCount:coreChallenge.previousFailureCount() + failureResponse:coreChallenge.failureResponse().nsURLResponse() + error:coreChallenge.error() + sender:coreChallenge.sender()] autorelease]; +} + +NSURLProtectionSpace *mac(const ProtectionSpace& coreSpace) +{ + NSString *proxyType = nil; + NSString *protocol = nil; + switch (coreSpace.serverType()) { + case ProtectionSpaceServerHTTP: + protocol = @"http"; + break; + case ProtectionSpaceServerHTTPS: + protocol = @"https"; + break; + case ProtectionSpaceServerFTP: + protocol = @"ftp"; + break; + case ProtectionSpaceServerFTPS: + protocol = @"ftps"; + break; + case ProtectionSpaceProxyHTTP: + proxyType = NSURLProtectionSpaceHTTPProxy; + break; + case ProtectionSpaceProxyHTTPS: + proxyType = NSURLProtectionSpaceHTTPSProxy; + break; + case ProtectionSpaceProxyFTP: + proxyType = NSURLProtectionSpaceFTPProxy; + break; + case ProtectionSpaceProxySOCKS: + proxyType = NSURLProtectionSpaceSOCKSProxy; + break; + default: + ASSERT_NOT_REACHED(); + } + + NSString *method = nil; + switch (coreSpace.authenticationScheme()) { + case ProtectionSpaceAuthenticationSchemeDefault: + method = NSURLAuthenticationMethodDefault; + break; + case ProtectionSpaceAuthenticationSchemeHTTPBasic: + method = NSURLAuthenticationMethodHTTPBasic; + break; + case ProtectionSpaceAuthenticationSchemeHTTPDigest: + method = NSURLAuthenticationMethodHTTPDigest; + break; + case ProtectionSpaceAuthenticationSchemeHTMLForm: + method = NSURLAuthenticationMethodHTMLForm; + break; + case ProtectionSpaceAuthenticationSchemeNTLM: + method = NSURLAuthenticationMethodNTLM; + break; +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + case ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested: + method = NSURLAuthenticationMethodServerTrust; + break; + case ProtectionSpaceAuthenticationSchemeClientCertificateRequested: + method = NSURLAuthenticationMethodClientCertificate; + break; +#endif + default: + ASSERT_NOT_REACHED(); + } + + if (proxyType) + return [[[NSURLProtectionSpace alloc] initWithProxyHost:coreSpace.host() + port:coreSpace.port() + type:proxyType + realm:coreSpace.realm() + authenticationMethod:method] autorelease]; + return [[[NSURLProtectionSpace alloc] initWithHost:coreSpace.host() + port:coreSpace.port() + protocol:protocol + realm:coreSpace.realm() + authenticationMethod:method] autorelease]; +} + +NSURLCredential *mac(const Credential& coreCredential) +{ + if (coreCredential.isEmpty()) + return nil; + + NSURLCredentialPersistence persistence = NSURLCredentialPersistenceNone; + switch (coreCredential.persistence()) { + case CredentialPersistenceNone: + break; + case CredentialPersistenceForSession: + persistence = NSURLCredentialPersistenceForSession; + break; + case CredentialPersistencePermanent: + persistence = NSURLCredentialPersistencePermanent; + break; + default: + ASSERT_NOT_REACHED(); + } + +#if CERTIFICATE_CREDENTIALS_SUPPORTED + if (coreCredential.type() == CredentialTypeClientCertificate) { + return [[[NSURLCredential alloc] initWithIdentity:coreCredential.identity() + certificates:(NSArray *)coreCredential.certificates() + persistence:persistence] + autorelease]; + } +#endif + + return [[[NSURLCredential alloc] initWithUser:coreCredential.user() + password:coreCredential.password() + persistence:persistence] + autorelease]; +} + +AuthenticationChallenge core(NSURLAuthenticationChallenge *macChallenge) +{ + return AuthenticationChallenge(macChallenge); +} + +ProtectionSpace core(NSURLProtectionSpace *macSpace) +{ + ProtectionSpaceServerType serverType = ProtectionSpaceProxyHTTP; + + if ([macSpace isProxy]) { + NSString *proxyType = [macSpace proxyType]; + if ([proxyType isEqualToString:NSURLProtectionSpaceHTTPProxy]) + serverType = ProtectionSpaceProxyHTTP; + else if ([proxyType isEqualToString:NSURLProtectionSpaceHTTPSProxy]) + serverType = ProtectionSpaceProxyHTTPS; + else if ([proxyType isEqualToString:NSURLProtectionSpaceFTPProxy]) + serverType = ProtectionSpaceProxyFTP; + else if ([proxyType isEqualToString:NSURLProtectionSpaceSOCKSProxy]) + serverType = ProtectionSpaceProxySOCKS; + else + ASSERT_NOT_REACHED(); + } else { + NSString *protocol = [macSpace protocol]; + if ([protocol caseInsensitiveCompare:@"http"] == NSOrderedSame) + serverType = ProtectionSpaceServerHTTP; + else if ([protocol caseInsensitiveCompare:@"https"] == NSOrderedSame) + serverType = ProtectionSpaceServerHTTPS; + else if ([protocol caseInsensitiveCompare:@"ftp"] == NSOrderedSame) + serverType = ProtectionSpaceServerFTP; + else if ([protocol caseInsensitiveCompare:@"ftps"] == NSOrderedSame) + serverType = ProtectionSpaceServerFTPS; + else + ASSERT_NOT_REACHED(); + } + + ProtectionSpaceAuthenticationScheme scheme = ProtectionSpaceAuthenticationSchemeDefault; + NSString *method = [macSpace authenticationMethod]; + if ([method isEqualToString:NSURLAuthenticationMethodDefault]) + scheme = ProtectionSpaceAuthenticationSchemeDefault; + else if ([method isEqualToString:NSURLAuthenticationMethodHTTPBasic]) + scheme = ProtectionSpaceAuthenticationSchemeHTTPBasic; + else if ([method isEqualToString:NSURLAuthenticationMethodHTTPDigest]) + scheme = ProtectionSpaceAuthenticationSchemeHTTPDigest; + else if ([method isEqualToString:NSURLAuthenticationMethodHTMLForm]) + scheme = ProtectionSpaceAuthenticationSchemeHTMLForm; + else if ([method isEqualToString:NSURLAuthenticationMethodNTLM]) + scheme = ProtectionSpaceAuthenticationSchemeNTLM; +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + else if ([method isEqualToString:NSURLAuthenticationMethodClientCertificate]) + scheme = ProtectionSpaceAuthenticationSchemeClientCertificateRequested; + else if ([method isEqualToString:NSURLAuthenticationMethodServerTrust]) + scheme = ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested; +#endif + else { + scheme = ProtectionSpaceAuthenticationSchemeUnknown; + ASSERT_NOT_REACHED(); + } + + return ProtectionSpace([macSpace host], [macSpace port], serverType, [macSpace realm], scheme); + +} + +Credential core(NSURLCredential *macCredential) +{ + CredentialPersistence persistence = CredentialPersistenceNone; + switch ([macCredential persistence]) { + case NSURLCredentialPersistenceNone: + break; + case NSURLCredentialPersistenceForSession: + persistence = CredentialPersistenceForSession; + break; + case NSURLCredentialPersistencePermanent: + persistence = CredentialPersistencePermanent; + break; + default: + ASSERT_NOT_REACHED(); + } + +#if CERTIFICATE_CREDENTIALS_SUPPORTED + SecIdentityRef identity = [macCredential identity]; + if (identity) + return Credential(identity, (CFArrayRef)[macCredential certificates], persistence); +#endif + + return Credential([macCredential user], [macCredential password], persistence); +} + +} // namespace WebCore + +#endif // !USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/mac/CookieStorageMac.mm b/Source/WebCore/platform/network/mac/CookieStorageMac.mm new file mode 100644 index 0000000..ab26f7b --- /dev/null +++ b/Source/WebCore/platform/network/mac/CookieStorageMac.mm @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008 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 COMPUTER, 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 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 "CookieStorage.h" + +#import "WebCoreSystemInterface.h" + +namespace WebCore { + +void setCookieStoragePrivateBrowsingEnabled(bool enabled) +{ + wkSetCookieStoragePrivateBrowsingEnabled(enabled); +} + +} diff --git a/Source/WebCore/platform/network/mac/CredentialStorageMac.mm b/Source/WebCore/platform/network/mac/CredentialStorageMac.mm new file mode 100644 index 0000000..66e94e9 --- /dev/null +++ b/Source/WebCore/platform/network/mac/CredentialStorageMac.mm @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009 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 "CredentialStorage.h" + +#include "AuthenticationMac.h" +#include "Credential.h" + +namespace WebCore { + +Credential CredentialStorage::getFromPersistentStorage(const ProtectionSpace& protectionSpace) +{ + NSURLCredential *credential = [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:mac(protectionSpace)]; + return credential ? core(credential) : Credential(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/mac/FormDataStreamMac.h b/Source/WebCore/platform/network/mac/FormDataStreamMac.h new file mode 100644 index 0000000..4bc9968 --- /dev/null +++ b/Source/WebCore/platform/network/mac/FormDataStreamMac.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005, 2006, 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 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 FormDataStreamMac_h +#define FormDataStreamMac_h + +#if !USE(CFNETWORK) + +#include "FormData.h" + +@class NSMutableURLRequest; + +namespace WebCore { + + class FormData; + class ResourceHandle; + + void setHTTPBody(NSMutableURLRequest *, PassRefPtr<FormData>); + FormData* httpBodyFromStream(NSInputStream *); + + void associateStreamWithResourceHandle(NSInputStream *, ResourceHandle*); + void disassociateStreamWithResourceHandle(NSInputStream *); + +} // namespace WebCore + +#endif // !USE(CFNETWORK) + +#endif // FormDataStreamMac_h diff --git a/Source/WebCore/platform/network/mac/FormDataStreamMac.mm b/Source/WebCore/platform/network/mac/FormDataStreamMac.mm new file mode 100644 index 0000000..03f4579 --- /dev/null +++ b/Source/WebCore/platform/network/mac/FormDataStreamMac.mm @@ -0,0 +1,490 @@ +/* + * Copyright (C) 2005, 2006, 2008 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 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. + */ + +/* originally written by Becky Willrich, additional code by Darin Adler */ + +#import "config.h" +#import "FormDataStreamMac.h" + +#if !USE(CFNETWORK) + +#import "BlobRegistryImpl.h" +#import "FileSystem.h" +#import "FormData.h" +#import "ResourceHandle.h" +#import "ResourceHandleClient.h" +#import "SchedulePair.h" +#import "WebCoreSystemInterface.h" +#import <sys/stat.h> +#import <sys/types.h> +#import <wtf/Assertions.h> +#import <wtf/HashMap.h> +#import <wtf/MainThread.h> +#import <wtf/StdLibExtras.h> +#import <wtf/Threading.h> + +#if PLATFORM(IOS) +#import <MacErrors.h> +#else +#import <CoreServices/CoreServices.h> +#endif + +namespace WebCore { + +typedef HashMap<CFReadStreamRef, RefPtr<FormData> > StreamFormDataMap; +static StreamFormDataMap& getStreamFormDataMap() +{ + DEFINE_STATIC_LOCAL(StreamFormDataMap, streamFormDataMap, ()); + return streamFormDataMap; +} + +typedef HashMap<CFReadStreamRef, RefPtr<ResourceHandle> > StreamResourceHandleMap; +static StreamResourceHandleMap& getStreamResourceHandleMap() +{ + DEFINE_STATIC_LOCAL(StreamResourceHandleMap, streamResourceHandleMap, ()); + return streamResourceHandleMap; +} + +void associateStreamWithResourceHandle(NSInputStream *stream, ResourceHandle* resourceHandle) +{ + ASSERT(isMainThread()); + + ASSERT(resourceHandle); + + if (!stream) + return; + + if (!getStreamFormDataMap().contains((CFReadStreamRef)stream)) + return; + + ASSERT(!getStreamResourceHandleMap().contains((CFReadStreamRef)stream)); + getStreamResourceHandleMap().set((CFReadStreamRef)stream, resourceHandle); +} + +void disassociateStreamWithResourceHandle(NSInputStream *stream) +{ + ASSERT(isMainThread()); + + if (!stream) + return; + + getStreamResourceHandleMap().remove((CFReadStreamRef)stream); +} + +struct DidSendDataCallbackData { + DidSendDataCallbackData(CFReadStreamRef stream_, unsigned long long bytesSent_, unsigned long long streamLength_) + : stream(stream_) + , bytesSent(bytesSent_) + , streamLength(streamLength_) + { + } + + CFReadStreamRef stream; + unsigned long long bytesSent; + unsigned long long streamLength; +}; + +static void performDidSendDataCallback(void* context) +{ + ASSERT(isMainThread()); + + DidSendDataCallbackData* data = static_cast<DidSendDataCallbackData*>(context); + ResourceHandle* resourceHandle = getStreamResourceHandleMap().get(data->stream).get(); + + if (resourceHandle && resourceHandle->client()) + resourceHandle->client()->didSendData(resourceHandle, data->bytesSent, data->streamLength); + + delete data; +} + +static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context); + +struct FormContext { + FormData* formData; + unsigned long long streamLength; +}; + +struct FormStreamFields { + SchedulePairHashSet scheduledRunLoopPairs; + Vector<FormDataElement> remainingElements; // in reverse order + CFReadStreamRef currentStream; +#if ENABLE(BLOB) + long long currentStreamRangeLength; +#endif + char* currentData; + CFReadStreamRef formStream; + unsigned long long streamLength; + unsigned long long bytesSent; +}; + +static void closeCurrentStream(FormStreamFields *form) +{ + if (form->currentStream) { + CFReadStreamClose(form->currentStream); + CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL); + CFRelease(form->currentStream); + form->currentStream = NULL; +#if ENABLE(BLOB) + form->currentStreamRangeLength = BlobDataItem::toEndOfFile; +#endif + } + if (form->currentData) { + fastFree(form->currentData); + form->currentData = 0; + } +} + +// Return false if we cannot advance the stream. Currently the only possible failure is that the underlying file has been removed or changed since File.slice. +static bool advanceCurrentStream(FormStreamFields* form) +{ + closeCurrentStream(form); + + if (form->remainingElements.isEmpty()) + return true; + + // Create the new stream. + FormDataElement& nextInput = form->remainingElements.last(); + + if (nextInput.m_type == FormDataElement::data) { + size_t size = nextInput.m_data.size(); + char* data = nextInput.m_data.releaseBuffer(); + form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull); + form->currentData = data; + } else { +#if ENABLE(BLOB) + // Check if the file has been changed or not if required. + if (nextInput.m_expectedFileModificationTime != BlobDataItem::doNotCheckFileChange) { + time_t fileModificationTime; + if (!getFileModificationTime(nextInput.m_filename, fileModificationTime) || fileModificationTime != static_cast<time_t>(nextInput.m_expectedFileModificationTime)) + return false; + } +#endif + const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename; + RetainPtr<CFStringRef> filename(AdoptCF, path.createCFString()); + RetainPtr<CFURLRef> fileURL(AdoptCF, CFURLCreateWithFileSystemPath(0, filename.get(), kCFURLPOSIXPathStyle, FALSE)); + form->currentStream = CFReadStreamCreateWithFile(0, fileURL.get()); + if (!form->currentStream) { + // The file must have been removed or become unreadable. + return false; + } +#if ENABLE(BLOB) + if (nextInput.m_fileStart > 0) { + CFNumberRef position = CFNumberCreate(0, kCFNumberLongLongType, &nextInput.m_fileStart); + CFReadStreamSetProperty(form->currentStream, kCFStreamPropertyFileCurrentOffset, position); + } + form->currentStreamRangeLength = nextInput.m_fileLength; +#endif + } + form->remainingElements.removeLast(); + + // Set up the callback. + CFStreamClientContext context = { 0, form, NULL, NULL, NULL }; + CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, + formEventCallback, &context); + + // Schedule with the current set of run loops. + SchedulePairHashSet::iterator end = form->scheduledRunLoopPairs.end(); + for (SchedulePairHashSet::iterator it = form->scheduledRunLoopPairs.begin(); it != end; ++it) + CFReadStreamScheduleWithRunLoop(form->currentStream, (*it)->runLoop(), (*it)->mode()); + + return true; +} + +static bool openNextStream(FormStreamFields* form) +{ + // Skip over any streams we can't open. + if (!advanceCurrentStream(form)) + return false; + while (form->currentStream && !CFReadStreamOpen(form->currentStream)) { + if (!advanceCurrentStream(form)) + return false; + } + return true; +} + +static void* formCreate(CFReadStreamRef stream, void* context) +{ + FormContext* formContext = static_cast<FormContext*>(context); + + FormStreamFields* newInfo = new FormStreamFields; + newInfo->currentStream = NULL; +#if ENABLE(BLOB) + newInfo->currentStreamRangeLength = BlobDataItem::toEndOfFile; +#endif + newInfo->currentData = 0; + newInfo->formStream = stream; // Don't retain. That would create a reference cycle. + newInfo->streamLength = formContext->streamLength; + newInfo->bytesSent = 0; + + FormData* formData = formContext->formData; + + // Append in reverse order since we remove elements from the end. + size_t size = formData->elements().size(); + newInfo->remainingElements.reserveInitialCapacity(size); + for (size_t i = 0; i < size; ++i) + newInfo->remainingElements.append(formData->elements()[size - i - 1]); + + getStreamFormDataMap().set(stream, adoptRef(formData)); + + return newInfo; +} + +static void formFinalize(CFReadStreamRef stream, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + getStreamFormDataMap().remove(stream); + + closeCurrentStream(form); + delete form; +} + +static Boolean formOpen(CFReadStreamRef, CFStreamError* error, Boolean* openComplete, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + bool opened = openNextStream(form); + + *openComplete = opened; + error->error = opened ? 0 : fnfErr; + return opened; +} + +static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + while (form->currentStream) { + CFIndex bytesToRead = bufferLength; +#if ENABLE(BLOB) + if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile && form->currentStreamRangeLength < bytesToRead) + bytesToRead = static_cast<CFIndex>(form->currentStreamRangeLength); +#endif + CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bytesToRead); + if (bytesRead < 0) { + *error = CFReadStreamGetError(form->currentStream); + return -1; + } + if (bytesRead > 0) { + error->error = 0; + *atEOF = FALSE; + form->bytesSent += bytesRead; +#if ENABLE(BLOB) + if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile) + form->currentStreamRangeLength -= bytesRead; +#endif + + if (!ResourceHandle::didSendBodyDataDelegateExists()) { + // FIXME: Figure out how to only do this when a ResourceHandleClient is available. + DidSendDataCallbackData* data = new DidSendDataCallbackData(stream, form->bytesSent, form->streamLength); + callOnMainThread(performDidSendDataCallback, data); + } + + return bytesRead; + } + openNextStream(form); + } + + error->error = 0; + *atEOF = TRUE; + return 0; +} + +static Boolean formCanRead(CFReadStreamRef stream, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) { + openNextStream(form); + } + if (!form->currentStream) { + wkSignalCFReadStreamEnd(stream); + return FALSE; + } + return CFReadStreamHasBytesAvailable(form->currentStream); +} + +static void formClose(CFReadStreamRef, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + closeCurrentStream(form); +} + +static void formSchedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + if (form->currentStream) + CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode); + form->scheduledRunLoopPairs.add(SchedulePair::create(runLoop, runLoopMode)); +} + +static void formUnschedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + if (form->currentStream) + CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode); + form->scheduledRunLoopPairs.remove(SchedulePair::create(runLoop, runLoopMode)); +} + +static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context) +{ + FormStreamFields* form = static_cast<FormStreamFields*>(context); + + switch (type) { + case kCFStreamEventHasBytesAvailable: + wkSignalCFReadStreamHasBytes(form->formStream); + break; + case kCFStreamEventErrorOccurred: { + CFStreamError readStreamError = CFReadStreamGetError(stream); + wkSignalCFReadStreamError(form->formStream, &readStreamError); + break; + } + case kCFStreamEventEndEncountered: + openNextStream(form); + if (!form->currentStream) { + wkSignalCFReadStreamEnd(form->formStream); + } + break; + case kCFStreamEventNone: + LOG_ERROR("unexpected kCFStreamEventNone"); + break; + case kCFStreamEventOpenCompleted: + LOG_ERROR("unexpected kCFStreamEventOpenCompleted"); + break; + case kCFStreamEventCanAcceptBytes: + LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes"); + break; + } +} + +void setHTTPBody(NSMutableURLRequest *request, PassRefPtr<FormData> formData) +{ + if (!formData) + return; + + size_t count = formData->elements().size(); + + // Handle the common special case of one piece of form data, with no files. + if (count == 1 && !formData->alwaysStream()) { + const FormDataElement& element = formData->elements()[0]; + if (element.m_type == FormDataElement::data) { + NSData *data = [[NSData alloc] initWithBytes:element.m_data.data() length:element.m_data.size()]; + [request setHTTPBody:data]; + [data release]; + return; + } + } + +#if ENABLE(BLOB) + // Check if there is a blob in the form data. + bool hasBlob = false; + for (size_t i = 0; i < count; ++i) { + const FormDataElement& element = formData->elements()[i]; + if (element.m_type == FormDataElement::encodedBlob) { + hasBlob = true; + break; + } + } + + // If yes, we have to resolve all the blob references and regenerate the form data with only data and file types. + if (hasBlob) { + RefPtr<FormData> newFormData = FormData::create(); + newFormData->setAlwaysStream(formData->alwaysStream()); + newFormData->setIdentifier(formData->identifier()); + for (size_t i = 0; i < count; ++i) { + const FormDataElement& element = formData->elements()[i]; + if (element.m_type == FormDataElement::data) + newFormData->appendData(element.m_data.data(), element.m_data.size()); + else if (element.m_type == FormDataElement::encodedFile) + newFormData->appendFile(element.m_filename, element.m_shouldGenerateFile); + else { + ASSERT(element.m_type == FormDataElement::encodedBlob); + RefPtr<BlobStorageData> blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(KURL(ParsedURLString, element.m_blobURL)); + if (blobData) { + 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)); + } else { + ASSERT(blobItem.type == BlobDataItem::File); + newFormData->appendFileRange(blobItem.path, blobItem.offset, blobItem.length, blobItem.expectedModificationTime); + } + } + } + } + } + formData = newFormData; + count = formData->elements().size(); + } +#endif + + // Precompute the content length so NSURLConnection doesn't use chunked mode. + long long length = 0; + for (size_t i = 0; i < count; ++i) { + const FormDataElement& element = formData->elements()[i]; + if (element.m_type == FormDataElement::data) + length += element.m_data.size(); + else { +#if ENABLE(BLOB) + // If we're sending the file range, use the existing range length for now. We will detect if the file has been changed right before we read the file and abort the operation if necessary. + if (element.m_fileLength != BlobDataItem::toEndOfFile) { + length += element.m_fileLength; + continue; + } +#endif + long long fileSize; + if (getFileSize(element.m_shouldGenerateFile ? element.m_generatedFilename : element.m_filename, fileSize)) + length += fileSize; + } + } + + // Set the length. + [request setValue:[NSString stringWithFormat:@"%lld", length] forHTTPHeaderField:@"Content-Length"]; + + // Create and set the stream. + + // Pass the length along with the formData so it does not have to be recomputed. + FormContext formContext = { formData.releaseRef(), length }; + + RetainPtr<CFReadStreamRef> stream(AdoptCF, wkCreateCustomCFReadStream(formCreate, formFinalize, + formOpen, formRead, formCanRead, formClose, formSchedule, formUnschedule, + &formContext)); + [request setHTTPBodyStream:(NSInputStream *)stream.get()]; +} + +FormData* httpBodyFromStream(NSInputStream* stream) +{ + return getStreamFormDataMap().get((CFReadStreamRef)stream).get(); +} + +} // namespace WebCore + +#endif // !USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/mac/NetworkStateNotifierMac.cpp b/Source/WebCore/platform/network/mac/NetworkStateNotifierMac.cpp new file mode 100644 index 0000000..2045eb3 --- /dev/null +++ b/Source/WebCore/platform/network/mac/NetworkStateNotifierMac.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2008 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. ``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. + */ + +#include "config.h" +#include "NetworkStateNotifier.h" + +#include <SystemConfiguration/SystemConfiguration.h> + +#ifdef BUILDING_ON_TIGER +// This function is available on Tiger, but not declared in the CFRunLoop.h header on Tiger. +extern "C" CFRunLoopRef CFRunLoopGetMain(); +#endif + +namespace WebCore { + +static const double StateChangeTimerInterval = 2.0; + +void NetworkStateNotifier::updateState() +{ + // Assume that we're offline until proven otherwise. + m_isOnLine = false; + + RetainPtr<CFStringRef> str(AdoptCF, SCDynamicStoreKeyCreateNetworkInterface(0, kSCDynamicStoreDomainState)); + + RetainPtr<CFPropertyListRef> propertyList(AdoptCF, SCDynamicStoreCopyValue(m_store.get(), str.get())); + + if (!propertyList) + return; + + if (CFGetTypeID(propertyList.get()) != CFDictionaryGetTypeID()) + return; + + CFArrayRef netInterfaces = (CFArrayRef)CFDictionaryGetValue((CFDictionaryRef)propertyList.get(), kSCDynamicStorePropNetInterfaces); + if (CFGetTypeID(netInterfaces) != CFArrayGetTypeID()) + return; + + for (CFIndex i = 0; i < CFArrayGetCount(netInterfaces); i++) { + CFStringRef interface = (CFStringRef)CFArrayGetValueAtIndex(netInterfaces, i); + if (CFGetTypeID(interface) != CFStringGetTypeID()) + continue; + + // Ignore the loopback interface. + if (CFStringFind(interface, CFSTR("lo"), kCFCompareAnchored).location != kCFNotFound) + continue; + + RetainPtr<CFStringRef> key(AdoptCF, SCDynamicStoreKeyCreateNetworkInterfaceEntity(0, kSCDynamicStoreDomainState, interface, kSCEntNetIPv4)); + + RetainPtr<CFArrayRef> keyList(AdoptCF, SCDynamicStoreCopyKeyList(m_store.get(), key.get())); + + if (keyList && CFArrayGetCount(keyList.get())) { + m_isOnLine = true; + break; + } + } +} + +void NetworkStateNotifier::dynamicStoreCallback(SCDynamicStoreRef, CFArrayRef, void* info) +{ + NetworkStateNotifier* notifier = static_cast<NetworkStateNotifier*>(info); + + // Calling updateState() could be expensive so we schedule a timer that will do it + // when things have cooled down. + notifier->m_networkStateChangeTimer.startOneShot(StateChangeTimerInterval); +} + +void NetworkStateNotifier::networkStateChangeTimerFired(Timer<NetworkStateNotifier>*) +{ + bool oldOnLine = m_isOnLine; + + updateState(); + + if (m_isOnLine == oldOnLine) + return; + + if (m_networkStateChangedFunction) + m_networkStateChangedFunction(); +} + +NetworkStateNotifier::NetworkStateNotifier() + : m_isOnLine(false) + , m_networkStateChangedFunction(0) + , m_networkStateChangeTimer(this, &NetworkStateNotifier::networkStateChangeTimerFired) +{ + SCDynamicStoreContext context = { 0, this, 0, 0, 0 }; + + m_store.adoptCF(SCDynamicStoreCreate(0, CFSTR("com.apple.WebCore"), dynamicStoreCallback, &context)); + if (!m_store) + return; + + RetainPtr<CFRunLoopSourceRef> configSource = SCDynamicStoreCreateRunLoopSource(0, m_store.get(), 0); + if (!configSource) + return; + + CFRunLoopAddSource(CFRunLoopGetMain(), configSource.get(), kCFRunLoopCommonModes); + + RetainPtr<CFMutableArrayRef> keys(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks)); + RetainPtr<CFMutableArrayRef> patterns(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks)); + + RetainPtr<CFStringRef> key; + RetainPtr<CFStringRef> pattern; + + key.adoptCF(SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetIPv4)); + CFArrayAppendValue(keys.get(), key.get()); + + pattern.adoptCF(SCDynamicStoreKeyCreateNetworkInterfaceEntity(0, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4)); + CFArrayAppendValue(patterns.get(), pattern.get()); + + key.adoptCF(SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetDNS)); + CFArrayAppendValue(keys.get(), key.get()); + + SCDynamicStoreSetNotificationKeys(m_store.get(), keys.get(), patterns.get()); + + updateState(); +} + +} diff --git a/Source/WebCore/platform/network/mac/ResourceErrorMac.mm b/Source/WebCore/platform/network/mac/ResourceErrorMac.mm new file mode 100644 index 0000000..275ca41 --- /dev/null +++ b/Source/WebCore/platform/network/mac/ResourceErrorMac.mm @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2006, 2008 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 COMPUTER, 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 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. + */ + +#import "config.h" +#import "ResourceError.h" + +#import "BlockExceptions.h" +#import "KURL.h" +#import <Foundation/Foundation.h> + +@interface NSError (WebExtras) +- (NSString *)_web_localizedDescription; +@end + +namespace WebCore { + +void ResourceError::platformLazyInit() +{ + if (m_dataIsUpToDate) + return; + + m_domain = [m_platformError.get() domain]; + m_errorCode = [m_platformError.get() code]; + + NSString* failingURLString = [[m_platformError.get() userInfo] valueForKey:@"NSErrorFailingURLStringKey"]; + if (!failingURLString) + failingURLString = [[[m_platformError.get() userInfo] valueForKey:@"NSErrorFailingURLKey"] absoluteString]; + m_failingURL = failingURLString; + // Workaround for <rdar://problem/6554067> + m_localizedDescription = m_failingURL; + BEGIN_BLOCK_OBJC_EXCEPTIONS; + m_localizedDescription = [m_platformError.get() _web_localizedDescription]; + END_BLOCK_OBJC_EXCEPTIONS; + + m_dataIsUpToDate = true; +} + +bool ResourceError::platformCompare(const ResourceError& a, const ResourceError& b) +{ + return (NSError*)a == (NSError*)b; +} + +ResourceError::operator NSError*() const +{ + if (m_isNull) { + ASSERT(!m_platformError); + return nil; + } + + if (!m_platformError) { + RetainPtr<NSMutableDictionary> userInfo(AdoptNS, [[NSMutableDictionary alloc] init]); + + if (!m_localizedDescription.isEmpty()) + [userInfo.get() setValue:m_localizedDescription forKey:NSLocalizedDescriptionKey]; + + if (!m_failingURL.isEmpty()) { + NSURL *cocoaURL = KURL(ParsedURLString, m_failingURL); + [userInfo.get() setValue:m_failingURL forKey:@"NSErrorFailingURLStringKey"]; + [userInfo.get() setValue:cocoaURL forKey:@"NSErrorFailingURLKey"]; + } + + m_platformError.adoptNS([[NSError alloc] initWithDomain:m_domain code:m_errorCode userInfo:userInfo.get()]); + } + + return m_platformError.get(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/mac/ResourceHandleMac.mm b/Source/WebCore/platform/network/mac/ResourceHandleMac.mm new file mode 100644 index 0000000..84b656c --- /dev/null +++ b/Source/WebCore/platform/network/mac/ResourceHandleMac.mm @@ -0,0 +1,1081 @@ +/* + * Copyright (C) 2004, 2006, 2007, 2008, 2009, 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 COMPUTER, 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 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. + */ + +#import "config.h" +#import "ResourceHandleInternal.h" + +#if !USE(CFNETWORK) + +#import "AuthenticationChallenge.h" +#import "AuthenticationMac.h" +#import "Base64.h" +#import "BlobRegistry.h" +#import "BlockExceptions.h" +#import "CredentialStorage.h" +#import "CachedResourceLoader.h" +#import "EmptyProtocolDefinitions.h" +#import "FormDataStreamMac.h" +#import "Frame.h" +#import "FrameLoader.h" +#import "Logging.h" +#import "MIMETypeRegistry.h" +#import "Page.h" +#import "ResourceError.h" +#import "ResourceResponse.h" +#import "SchedulePair.h" +#import "Settings.h" +#import "SharedBuffer.h" +#import "SubresourceLoader.h" +#import "WebCoreSystemInterface.h" +#import "WebCoreURLResponse.h" +#import <wtf/text/CString.h> +#import <wtf/UnusedParam.h> + +#ifdef BUILDING_ON_TIGER +typedef int NSInteger; +#endif + +using namespace WebCore; + +@interface WebCoreResourceHandleAsDelegate : NSObject <NSURLConnectionDelegate> { + ResourceHandle* m_handle; +} +- (id)initWithHandle:(ResourceHandle*)handle; +- (void)detachHandle; +@end + +// WebCoreNSURLConnectionDelegateProxy exists so that we can cast m_proxy to it in order +// to disambiguate the argument type in the -setDelegate: call. This avoids a spurious +// warning that the compiler would otherwise emit. +@interface WebCoreNSURLConnectionDelegateProxy : NSObject <NSURLConnectionDelegate> +- (void)setDelegate:(id<NSURLConnectionDelegate>)delegate; +@end + +@interface NSURLConnection (NSURLConnectionTigerPrivate) +- (NSData *)_bufferedData; +@end + +@interface NSURLConnection (Details) +-(id)_initWithRequest:(NSURLRequest *)request delegate:(id)delegate usesCache:(BOOL)usesCacheFlag maxContentLength:(long long)maxContentLength startImmediately:(BOOL)startImmediately connectionProperties:(NSDictionary *)connectionProperties; +@end + +@interface NSURLRequest (Details) +- (id)_propertyForKey:(NSString *)key; +@end + +#ifndef BUILDING_ON_TIGER + +class WebCoreSynchronousLoaderClient : public ResourceHandleClient { +public: + static PassOwnPtr<WebCoreSynchronousLoaderClient> create() + { + return adoptPtr(new WebCoreSynchronousLoaderClient); + } + + virtual ~WebCoreSynchronousLoaderClient(); + + void setAllowStoredCredentials(bool allow) { m_allowStoredCredentials = allow; } + NSURLResponse *response() { return m_response; } + NSMutableData *data() { return m_data; } + NSError *error() { return m_error; } + bool isDone() { return m_isDone; } + +private: + WebCoreSynchronousLoaderClient() + : m_allowStoredCredentials(false) + , m_response(0) + , m_data(0) + , m_error(0) + , m_isDone(false) + { + } + + virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/); + virtual bool shouldUseCredentialStorage(ResourceHandle*); + virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge&); + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int /*lengthReceived*/); + virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/); + virtual void didFail(ResourceHandle*, const ResourceError&); +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + virtual bool canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&); +#endif + + bool m_allowStoredCredentials; + NSURLResponse *m_response; + NSMutableData *m_data; + NSError *m_error; + bool m_isDone; +}; + +static NSString *WebCoreSynchronousLoaderRunLoopMode = @"WebCoreSynchronousLoaderRunLoopMode"; + +#endif + +namespace WebCore { + +#ifdef BUILDING_ON_TIGER +static unsigned inNSURLConnectionCallback; +#endif + +#ifndef NDEBUG +static bool isInitializingConnection; +#endif + +class CallbackGuard { +public: + CallbackGuard() + { +#ifdef BUILDING_ON_TIGER + ++inNSURLConnectionCallback; +#endif + } + ~CallbackGuard() + { +#ifdef BUILDING_ON_TIGER + ASSERT(inNSURLConnectionCallback > 0); + --inNSURLConnectionCallback; +#endif + } +}; + +#ifndef BUILDING_ON_TIGER +static String encodeBasicAuthorization(const String& user, const String& password) +{ + CString unencodedString = (user + ":" + password).utf8(); + Vector<char> unencoded(unencodedString.length()); + std::copy(unencodedString.data(), unencodedString.data() + unencodedString.length(), unencoded.begin()); + Vector<char> encoded; + base64Encode(unencoded, encoded); + return String(encoded.data(), encoded.size()); +} +#endif + +ResourceHandleInternal::~ResourceHandleInternal() +{ +} + +ResourceHandle::~ResourceHandle() +{ + releaseDelegate(); + d->m_currentWebChallenge.setAuthenticationClient(0); + + LOG(Network, "Handle %p destroyed", this); +} + +static const double MaxFoundationVersionWithoutdidSendBodyDataDelegate = 677.21; +bool ResourceHandle::didSendBodyDataDelegateExists() +{ + return NSFoundationVersionNumber > MaxFoundationVersionWithoutdidSendBodyDataDelegate; +} + +void ResourceHandle::createNSURLConnection(id delegate, bool shouldUseCredentialStorage, bool shouldContentSniff) +{ + // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made. + if ((!d->m_user.isEmpty() || !d->m_pass.isEmpty()) +#ifndef BUILDING_ON_TIGER + && !firstRequest().url().protocolInHTTPFamily() // On Tiger, always pass credentials in URL, so that they get stored even if the request gets cancelled right away. +#endif + ) { + KURL urlWithCredentials(firstRequest().url()); + urlWithCredentials.setUser(d->m_user); + urlWithCredentials.setPass(d->m_pass); + firstRequest().setURL(urlWithCredentials); + } + + // If a URL already has cookies, then we'll relax the 3rd party cookie policy and accept new cookies. + NSHTTPCookieStorage *sharedStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + if ([sharedStorage cookieAcceptPolicy] == NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain && [[sharedStorage cookiesForURL:firstRequest().url()] count]) + firstRequest().setFirstPartyForCookies(firstRequest().url()); + +#if !defined(BUILDING_ON_TIGER) + if (shouldUseCredentialStorage && firstRequest().url().protocolInHTTPFamily()) { + if (d->m_user.isEmpty() && d->m_pass.isEmpty()) { + // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, + // try and reuse the credential preemptively, as allowed by RFC 2617. + d->m_initialCredential = CredentialStorage::get(firstRequest().url()); + } else { + // If there is already a protection space known for the URL, update stored credentials before sending a request. + // This makes it possible to implement logout by sending an XMLHttpRequest with known incorrect credentials, and aborting it immediately + // (so that an authentication dialog doesn't pop up). + CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), firstRequest().url()); + } + } + + if (!d->m_initialCredential.isEmpty()) { + // FIXME: Support Digest authentication, and Proxy-Authorization. + String authHeader = "Basic " + encodeBasicAuthorization(d->m_initialCredential.user(), d->m_initialCredential.password()); + firstRequest().addHTTPHeaderField("Authorization", authHeader); + } + + NSURLRequest *nsRequest = firstRequest().nsURLRequest(); + if (!shouldContentSniff) { + NSMutableURLRequest *mutableRequest = [[nsRequest copy] autorelease]; + wkSetNSURLRequestShouldContentSniff(mutableRequest, NO); + nsRequest = mutableRequest; + } + +#if !defined(BUILDING_ON_LEOPARD) + ASSERT([NSURLConnection instancesRespondToSelector:@selector(_initWithRequest:delegate:usesCache:maxContentLength:startImmediately:connectionProperties:)]); + static bool supportsSettingConnectionProperties = true; +#else + static bool supportsSettingConnectionProperties = [NSURLConnection instancesRespondToSelector:@selector(_initWithRequest:delegate:usesCache:maxContentLength:startImmediately:connectionProperties:)]; +#endif + + if (supportsSettingConnectionProperties) { + NSDictionary *sessionID = shouldUseCredentialStorage ? [NSDictionary dictionary] : [NSDictionary dictionaryWithObject:@"WebKitPrivateSession" forKey:@"_kCFURLConnectionSessionID"]; + NSDictionary *propertyDictionary = [NSDictionary dictionaryWithObject:sessionID forKey:@"kCFURLConnectionSocketStreamProperties"]; + d->m_connection.adoptNS([[NSURLConnection alloc] _initWithRequest:nsRequest delegate:delegate usesCache:YES maxContentLength:0 startImmediately:NO connectionProperties:propertyDictionary]); + return; + } + + d->m_connection.adoptNS([[NSURLConnection alloc] initWithRequest:nsRequest delegate:delegate startImmediately:NO]); + return; + +#else + // Building on Tiger. Don't use WebCore credential storage, don't try to disable content sniffing. + UNUSED_PARAM(shouldUseCredentialStorage); + UNUSED_PARAM(shouldContentSniff); + d->m_connection.adoptNS([[NSURLConnection alloc] initWithRequest:firstRequest().nsURLRequest() delegate:delegate]); +#endif +} + +bool ResourceHandle::start(NetworkingContext* context) +{ + if (!context) + return false; + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + + // If NetworkingContext is invalid then we are no longer attached to a Page, + // this must be an attempted load from an unload event handler, so let's just block it. + if (!context->isValid()) + return false; + +#ifndef NDEBUG + isInitializingConnection = YES; +#endif + + ASSERT(!d->m_proxy); + d->m_proxy.adoptNS(wkCreateNSURLConnectionDelegateProxy()); + [static_cast<WebCoreNSURLConnectionDelegateProxy*>(d->m_proxy.get()) setDelegate:ResourceHandle::delegate()]; + + bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this); + + if (!ResourceHandle::didSendBodyDataDelegateExists()) + associateStreamWithResourceHandle([firstRequest().nsURLRequest() HTTPBodyStream], this); + +#ifdef BUILDING_ON_TIGER + // A conditional request sent by WebCore (e.g. to update appcache) can be for a resource that is not cacheable by NSURLConnection, + // which can get confused and fail to load it in this case. + if (firstRequest().isConditional()) + firstRequest().setCachePolicy(ReloadIgnoringCacheData); +#endif + + d->m_needsSiteSpecificQuirks = context->needsSiteSpecificQuirks(); + + createNSURLConnection( + d->m_proxy.get(), + shouldUseCredentialStorage, + d->m_shouldContentSniff || context->localFileContentSniffingEnabled()); + +#ifndef BUILDING_ON_TIGER + bool scheduled = false; + if (SchedulePairHashSet* scheduledPairs = context->scheduledRunLoopPairs()) { + SchedulePairHashSet::iterator end = scheduledPairs->end(); + for (SchedulePairHashSet::iterator it = scheduledPairs->begin(); it != end; ++it) { + if (NSRunLoop *runLoop = (*it)->nsRunLoop()) { + [connection() scheduleInRunLoop:runLoop forMode:(NSString *)(*it)->mode()]; + scheduled = true; + } + } + } + + // Start the connection if we did schedule with at least one runloop. + // We can't start the connection until we have one runloop scheduled. + if (scheduled) + [connection() start]; + else + d->m_startWhenScheduled = true; +#endif + +#ifndef NDEBUG + isInitializingConnection = NO; +#endif + + LOG(Network, "Handle %p starting connection %p for %@", this, connection(), firstRequest().nsURLRequest()); + + if (d->m_connection) { + if (d->m_defersLoading) + wkSetNSURLConnectionDefersCallbacks(connection(), YES); + + return true; + } + + END_BLOCK_OBJC_EXCEPTIONS; + + return false; +} + +void ResourceHandle::cancel() +{ + LOG(Network, "Handle %p cancel connection %p", this, d->m_connection.get()); + + // Leaks were seen on HTTP tests without this; can be removed once <rdar://problem/6886937> is fixed. + if (d->m_currentMacChallenge) + [[d->m_currentMacChallenge sender] cancelAuthenticationChallenge:d->m_currentMacChallenge]; + + if (!ResourceHandle::didSendBodyDataDelegateExists()) + disassociateStreamWithResourceHandle([firstRequest().nsURLRequest() HTTPBodyStream]); + [d->m_connection.get() cancel]; +} + +void ResourceHandle::platformSetDefersLoading(bool defers) +{ + if (d->m_connection) + wkSetNSURLConnectionDefersCallbacks(d->m_connection.get(), defers); +} + +void ResourceHandle::schedule(SchedulePair* pair) +{ +#ifndef BUILDING_ON_TIGER + NSRunLoop *runLoop = pair->nsRunLoop(); + if (!runLoop) + return; + [d->m_connection.get() scheduleInRunLoop:runLoop forMode:(NSString *)pair->mode()]; + if (d->m_startWhenScheduled) { + [d->m_connection.get() start]; + d->m_startWhenScheduled = false; + } +#else + UNUSED_PARAM(pair); +#endif +} + +void ResourceHandle::unschedule(SchedulePair* pair) +{ +#ifndef BUILDING_ON_TIGER + if (NSRunLoop *runLoop = pair->nsRunLoop()) + [d->m_connection.get() unscheduleFromRunLoop:runLoop forMode:(NSString *)pair->mode()]; +#else + UNUSED_PARAM(pair); +#endif +} + +WebCoreResourceHandleAsDelegate *ResourceHandle::delegate() +{ + if (!d->m_delegate) { + WebCoreResourceHandleAsDelegate *delegate = [[WebCoreResourceHandleAsDelegate alloc] initWithHandle:this]; + d->m_delegate = delegate; + [delegate release]; + } + return d->m_delegate.get(); +} + +void ResourceHandle::releaseDelegate() +{ + if (!d->m_delegate) + return; + if (d->m_proxy) + [d->m_proxy.get() setDelegate:nil]; + [d->m_delegate.get() detachHandle]; + d->m_delegate = nil; +} + +bool ResourceHandle::supportsBufferedData() +{ + static bool supportsBufferedData = [NSURLConnection instancesRespondToSelector:@selector(_bufferedData)]; + return supportsBufferedData; +} + +PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() +{ + if (ResourceHandle::supportsBufferedData()) + return SharedBuffer::wrapNSData([d->m_connection.get() _bufferedData]); + + return 0; +} + +id ResourceHandle::releaseProxy() +{ + id proxy = [[d->m_proxy.get() retain] autorelease]; + d->m_proxy = nil; + [proxy setDelegate:nil]; + return proxy; +} + +NSURLConnection *ResourceHandle::connection() const +{ + return d->m_connection.get(); +} + +bool ResourceHandle::loadsBlocked() +{ +#ifndef BUILDING_ON_TIGER + return false; +#else + // On Tiger, if we're in an NSURLConnection callback, that blocks all other NSURLConnection callbacks. + // On Leopard and newer, it blocks only callbacks on that same NSURLConnection object, which is not + // a problem in practice. + return inNSURLConnectionCallback != 0; +#endif +} + +bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame*) +{ +#ifndef BUILDING_ON_TIGER + request.setCachePolicy(ReturnCacheDataDontLoad); + NSURLResponse *nsURLResponse = nil; + BEGIN_BLOCK_OBJC_EXCEPTIONS; + + [NSURLConnection sendSynchronousRequest:request.nsURLRequest() returningResponse:&nsURLResponse error:nil]; + + END_BLOCK_OBJC_EXCEPTIONS; + + return nsURLResponse; +#else + // <rdar://problem/6803217> - Re-enable after <rdar://problem/6786454> is resolved. + UNUSED_PARAM(request); + return false; +#endif +} + +void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) +{ + LOG(Network, "ResourceHandle::loadResourceSynchronously:%@ allowStoredCredentials:%u", request.nsURLRequest(), storedCredentials); + +#if ENABLE(BLOB) + if (request.url().protocolIs("blob")) + if (blobRegistry().loadResourceSynchronously(request, error, response, data)) + return; +#endif + + NSError *nsError = nil; + NSURLResponse *nsURLResponse = nil; + NSData *result = nil; + + ASSERT(!request.isEmpty()); + +#ifndef BUILDING_ON_TIGER + OwnPtr<WebCoreSynchronousLoaderClient> client = WebCoreSynchronousLoaderClient::create(); + client->setAllowStoredCredentials(storedCredentials == AllowStoredCredentials); + + RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, client.get(), false /*defersLoading*/, true /*shouldContentSniff*/)); + + if (context && handle->d->m_scheduledFailureType != NoFailure) { + error = context->blockedError(request); + return; + } + + handle->createNSURLConnection( + handle->delegate(), // A synchronous request cannot turn into a download, so there is no need to proxy the delegate. + storedCredentials == AllowStoredCredentials, + handle->shouldContentSniff() || (context && context->localFileContentSniffingEnabled())); + + [handle->connection() scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:WebCoreSynchronousLoaderRunLoopMode]; + [handle->connection() start]; + + while (!client->isDone()) + [[NSRunLoop currentRunLoop] runMode:WebCoreSynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]]; + + result = client->data(); + nsURLResponse = client->response(); + nsError = client->error(); + + [handle->connection() cancel]; + +#else + UNUSED_PARAM(storedCredentials); + UNUSED_PARAM(context); + NSURLRequest *firstRequest = request.nsURLRequest(); + + // If a URL already has cookies, then we'll relax the 3rd party cookie policy and accept new cookies. + NSHTTPCookieStorage *sharedStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + if ([sharedStorage cookieAcceptPolicy] == NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain && [[sharedStorage cookiesForURL:[firstRequest URL]] count]) { + NSMutableURLRequest *mutableRequest = [[firstRequest mutableCopy] autorelease]; + [mutableRequest setMainDocumentURL:[mutableRequest URL]]; + firstRequest = mutableRequest; + } + + BEGIN_BLOCK_OBJC_EXCEPTIONS; + result = [NSURLConnection sendSynchronousRequest:firstRequest returningResponse:&nsURLResponse error:&nsError]; + END_BLOCK_OBJC_EXCEPTIONS; +#endif + + if (!nsError) + response = nsURLResponse; + else { + response = ResourceResponse(request.url(), String(), 0, String(), String()); + if ([nsError domain] == NSURLErrorDomain) + switch ([nsError code]) { + case NSURLErrorUserCancelledAuthentication: + // FIXME: we should really return the actual HTTP response, but sendSynchronousRequest doesn't provide us with one. + response.setHTTPStatusCode(401); + break; + default: + response.setHTTPStatusCode([nsError code]); + } + else + response.setHTTPStatusCode(404); + } + + data.resize([result length]); + memcpy(data.data(), [result bytes], [result length]); + + error = nsError; +} + +void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse) +{ + const KURL& url = request.url(); + d->m_user = url.user(); + d->m_pass = url.pass(); + d->m_lastHTTPMethod = request.httpMethod(); + request.removeCredentials(); + if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) + request.clearHTTPAuthorization(); + + client()->willSendRequest(this, request, redirectResponse); +} + +bool ResourceHandle::shouldUseCredentialStorage() +{ + if (client()) + return client()->shouldUseCredentialStorage(this); + + return false; +} + +void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge) +{ + ASSERT(!d->m_currentMacChallenge); + ASSERT(d->m_currentWebChallenge.isNull()); + // Since NSURLConnection networking relies on keeping a reference to the original NSURLAuthenticationChallenge, + // we make sure that is actually present + ASSERT(challenge.nsURLAuthenticationChallenge()); + + if (!d->m_user.isNull() && !d->m_pass.isNull()) { + NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:d->m_user + password:d->m_pass + persistence:NSURLCredentialPersistenceForSession]; + d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge(); + d->m_currentWebChallenge = challenge; + receivedCredential(challenge, core(credential)); + [credential release]; + // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly. + d->m_user = String(); + d->m_pass = String(); + return; + } + +#ifndef BUILDING_ON_TIGER + if (!client() || client()->shouldUseCredentialStorage(this)) { + if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) { + // The stored credential wasn't accepted, stop using it. + // There is a race condition here, since a different credential might have already been stored by another ResourceHandle, + // but the observable effect should be very minor, if any. + CredentialStorage::remove(challenge.protectionSpace()); + } + + if (!challenge.previousFailureCount()) { + Credential credential = CredentialStorage::get(challenge.protectionSpace()); + if (!credential.isEmpty() && credential != d->m_initialCredential) { + ASSERT(credential.persistence() == CredentialPersistenceNone); + if (challenge.failureResponse().httpStatusCode() == 401) { + // Store the credential back, possibly adding it as a default for this directory. + CredentialStorage::set(credential, challenge.protectionSpace(), firstRequest().url()); + } + [challenge.sender() useCredential:mac(credential) forAuthenticationChallenge:mac(challenge)]; + return; + } + } + } +#endif + + d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge(); + d->m_currentWebChallenge = core(d->m_currentMacChallenge); + d->m_currentWebChallenge.setAuthenticationClient(this); + + if (client()) + client()->didReceiveAuthenticationChallenge(this, d->m_currentWebChallenge); +} + +void ResourceHandle::didCancelAuthenticationChallenge(const AuthenticationChallenge& challenge) +{ + ASSERT(d->m_currentMacChallenge); + ASSERT(d->m_currentMacChallenge == challenge.nsURLAuthenticationChallenge()); + ASSERT(!d->m_currentWebChallenge.isNull()); + + if (client()) + client()->didCancelAuthenticationChallenge(this, challenge); +} + +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +bool ResourceHandle::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace) +{ + if (client()) + return client()->canAuthenticateAgainstProtectionSpace(this, protectionSpace); + + return false; +} +#endif + +void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge, const Credential& credential) +{ + ASSERT(!challenge.isNull()); + if (challenge != d->m_currentWebChallenge) + return; + + // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map. + if (credential.isEmpty()) { + receivedRequestToContinueWithoutCredential(challenge); + return; + } + +#ifdef BUILDING_ON_TIGER + if (credential.persistence() == CredentialPersistenceNone) { + // NSURLCredentialPersistenceNone doesn't work on Tiger, so we have to use session persistence. + Credential webCredential(credential.user(), credential.password(), CredentialPersistenceForSession); + [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge]; + } else +#else + if (credential.persistence() == CredentialPersistenceForSession && (!d->m_needsSiteSpecificQuirks || ![[[mac(challenge) protectionSpace] host] isEqualToString:@"gallery.me.com"])) { + // Manage per-session credentials internally, because once NSURLCredentialPersistenceForSession is used, there is no way + // to ignore it for a particular request (short of removing it altogether). + // <rdar://problem/6867598> gallery.me.com is temporarily whitelisted, so that QuickTime plug-in could see the credentials. + Credential webCredential(credential, CredentialPersistenceNone); + KURL urlToStore; + if (challenge.failureResponse().httpStatusCode() == 401) + urlToStore = firstRequest().url(); + CredentialStorage::set(webCredential, core([d->m_currentMacChallenge protectionSpace]), urlToStore); + [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge]; + } else +#endif + [[d->m_currentMacChallenge sender] useCredential:mac(credential) forAuthenticationChallenge:d->m_currentMacChallenge]; + + clearAuthentication(); +} + +void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge) +{ + ASSERT(!challenge.isNull()); + if (challenge != d->m_currentWebChallenge) + return; + + [[d->m_currentMacChallenge sender] continueWithoutCredentialForAuthenticationChallenge:d->m_currentMacChallenge]; + + clearAuthentication(); +} + +void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challenge) +{ + if (challenge != d->m_currentWebChallenge) + return; + + if (client()) + client()->receivedCancellation(this, challenge); +} + +} // namespace WebCore + +@implementation WebCoreResourceHandleAsDelegate + +- (id)initWithHandle:(ResourceHandle*)handle +{ + self = [self init]; + if (!self) + return nil; + m_handle = handle; + return self; +} + +- (void)detachHandle +{ + m_handle = 0; +} + +- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse +{ + UNUSED_PARAM(connection); + + // the willSendRequest call may cancel this load, in which case self could be deallocated + RetainPtr<WebCoreResourceHandleAsDelegate> protect(self); + + if (!m_handle || !m_handle->client()) + return nil; + + // See <rdar://problem/5380697> . This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often. + if (!redirectResponse) + return newRequest; + +#if !LOG_DISABLED + if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]]) + LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:%d, Location:<%@>", m_handle, connection, [newRequest description], static_cast<int>([(id)redirectResponse statusCode]), [[(id)redirectResponse allHeaderFields] objectForKey:@"Location"]); + else + LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:non-HTTP", m_handle, connection, [newRequest description]); +#endif + + if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)redirectResponse statusCode] == 307) { + String lastHTTPMethod = m_handle->lastHTTPMethod(); + if (!equalIgnoringCase(lastHTTPMethod, String([newRequest HTTPMethod]))) { + NSMutableURLRequest *mutableRequest = [newRequest mutableCopy]; + [mutableRequest setHTTPMethod:lastHTTPMethod]; + + FormData* body = m_handle->firstRequest().httpBody(); + if (!equalIgnoringCase(lastHTTPMethod, "GET") && body && !body->isEmpty()) + WebCore::setHTTPBody(mutableRequest, body); + + String originalContentType = m_handle->firstRequest().httpContentType(); + if (!originalContentType.isEmpty()) + [mutableRequest setValue:originalContentType forHTTPHeaderField:@"Content-Type"]; + + newRequest = [mutableRequest autorelease]; + } + } + + CallbackGuard guard; + ResourceRequest request = newRequest; + + // Should not set Referer after a redirect from a secure resource to non-secure one. + if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https")) + request.clearHTTPReferrer(); + + m_handle->willSendRequest(request, redirectResponse); + + if (!ResourceHandle::didSendBodyDataDelegateExists()) { + // The client may change the request's body stream, in which case we have to re-associate + // the handle with the new stream so upload progress callbacks continue to work correctly. + NSInputStream* oldBodyStream = [newRequest HTTPBodyStream]; + NSInputStream* newBodyStream = [request.nsURLRequest() HTTPBodyStream]; + if (oldBodyStream != newBodyStream) { + disassociateStreamWithResourceHandle(oldBodyStream); + associateStreamWithResourceHandle(newBodyStream, m_handle); + } + } + + return request.nsURLRequest(); +} + +- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection +{ + UNUSED_PARAM(connection); + + LOG(Network, "Handle %p delegate connectionShouldUseCredentialStorage:%p", m_handle, connection); + + if (!m_handle) + return NO; + + CallbackGuard guard; + return m_handle->shouldUseCredentialStorage(); +} + +- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + UNUSED_PARAM(connection); + + LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge); + + if (!m_handle) + return; + CallbackGuard guard; + m_handle->didReceiveAuthenticationChallenge(core(challenge)); +} + +- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + UNUSED_PARAM(connection); + + LOG(Network, "Handle %p delegate connection:%p didCancelAuthenticationChallenge:%p", m_handle, connection, challenge); + + if (!m_handle) + return; + CallbackGuard guard; + m_handle->didCancelAuthenticationChallenge(core(challenge)); +} + +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +- (BOOL)connection:(NSURLConnection *)unusedConnection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace +{ + UNUSED_PARAM(unusedConnection); + + if (!m_handle) + return NO; + + CallbackGuard guard; + return m_handle->canAuthenticateAgainstProtectionSpace(core(protectionSpace)); +} +#endif + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r +{ + UNUSED_PARAM(connection); + + LOG(Network, "Handle %p delegate connection:%p didReceiveResponse:%p (HTTP status %d, reported MIMEType '%s')", m_handle, connection, r, [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0, [[r MIMEType] UTF8String]); + + if (!m_handle || !m_handle->client()) + return; + CallbackGuard guard; + + // Avoid MIME type sniffing if the response comes back as 304 Not Modified. + int statusCode = [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0; + if (statusCode != 304) + [r adjustMIMETypeIfNecessary]; + + if ([m_handle->firstRequest().nsURLRequest() _propertyForKey:@"ForceHTMLMIMEType"]) + [r _setMIMEType:@"text/html"]; + +#if ENABLE(WML) + const KURL& url = [r URL]; + if (url.isLocalFile()) { + // FIXME: Workaround for <rdar://problem/6917571>: The WML file extension ".wml" is not mapped to + // the right MIME type, work around that CFNetwork problem, to unbreak WML support for local files. + const String& path = url.path(); + + DEFINE_STATIC_LOCAL(const String, wmlExt, (".wml")); + if (path.endsWith(wmlExt, false)) { + static NSString* defaultMIMETypeString = [(NSString*) defaultMIMEType() retain]; + if ([[r MIMEType] isEqualToString:defaultMIMETypeString]) + [r _setMIMEType:@"text/vnd.wap.wml"]; + } + } +#endif + + m_handle->client()->didReceiveResponse(m_handle, r); +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived +{ + UNUSED_PARAM(connection); + + LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived); + + if (!m_handle || !m_handle->client()) + return; + // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing. + // However, with today's computers and networking speeds, this won't happen in practice. + // Could be an issue with a giant local file. + CallbackGuard guard; + m_handle->client()->didReceiveData(m_handle, (const char*)[data bytes], [data length], static_cast<int>(lengthReceived)); +} + +- (void)connection:(NSURLConnection *)connection willStopBufferingData:(NSData *)data +{ + UNUSED_PARAM(connection); + + LOG(Network, "Handle %p delegate connection:%p willStopBufferingData:%p", m_handle, connection, data); + + if (!m_handle || !m_handle->client()) + return; + // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing. + // However, with today's computers and networking speeds, this won't happen in practice. + // Could be an issue with a giant local file. + CallbackGuard guard; + m_handle->client()->willStopBufferingData(m_handle, (const char*)[data bytes], static_cast<int>([data length])); +} + +- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite +{ + UNUSED_PARAM(connection); + UNUSED_PARAM(bytesWritten); + + LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); + + if (!m_handle || !m_handle->client()) + return; + CallbackGuard guard; + m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite); +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + UNUSED_PARAM(connection); + + LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection); + + if (!m_handle || !m_handle->client()) + return; + CallbackGuard guard; + + if (!ResourceHandle::didSendBodyDataDelegateExists()) + disassociateStreamWithResourceHandle([m_handle->firstRequest().nsURLRequest() HTTPBodyStream]); + + m_handle->client()->didFinishLoading(m_handle, 0); +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + UNUSED_PARAM(connection); + + LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error); + + if (!m_handle || !m_handle->client()) + return; + CallbackGuard guard; + + if (!ResourceHandle::didSendBodyDataDelegateExists()) + disassociateStreamWithResourceHandle([m_handle->firstRequest().nsURLRequest() HTTPBodyStream]); + + m_handle->client()->didFail(m_handle, error); +} + +#ifdef BUILDING_ON_TIGER +- (void)_callConnectionWillCacheResponseWithInfo:(NSMutableDictionary *)info +{ + NSURLConnection *connection = [info objectForKey:@"connection"]; + NSCachedURLResponse *cachedResponse = [info objectForKey:@"cachedResponse"]; + NSCachedURLResponse *result = [self connection:connection willCacheResponse:cachedResponse]; + if (result) + [info setObject:result forKey:@"result"]; +} +#endif + +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ + LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse); + +#ifdef BUILDING_ON_TIGER + // On Tiger CFURLConnection can sometimes call the connection:willCacheResponse: delegate method on + // a secondary thread instead of the main thread. If this happens perform the work on the main thread. + if (!pthread_main_np()) { + NSMutableDictionary *info = [[NSMutableDictionary alloc] init]; + if (connection) + [info setObject:connection forKey:@"connection"]; + if (cachedResponse) + [info setObject:cachedResponse forKey:@"cachedResponse"]; + + // Include synchronous url connection's mode as an acceptable run loopmode + // <rdar://problem/5511842> + NSArray *modes = [[NSArray alloc] initWithObjects:(NSString *)kCFRunLoopCommonModes, @"NSSynchronousURLConnection_PrivateMode", nil]; + [self performSelectorOnMainThread:@selector(_callConnectionWillCacheResponseWithInfo:) withObject:info waitUntilDone:YES modes:modes]; + [modes release]; + + NSCachedURLResponse *result = [[info valueForKey:@"result"] retain]; + [info release]; + + return [result autorelease]; + } +#else + UNUSED_PARAM(connection); +#endif + +#ifndef NDEBUG + if (isInitializingConnection) + LOG_ERROR("connection:willCacheResponse: was called inside of [NSURLConnection initWithRequest:delegate:] (4067625)"); +#endif + + if (!m_handle || !m_handle->client()) + return nil; + + CallbackGuard guard; + + NSCachedURLResponse *newResponse = m_handle->client()->willCacheResponse(m_handle, cachedResponse); + if (newResponse != cachedResponse) + return newResponse; + + CacheStoragePolicy policy = static_cast<CacheStoragePolicy>([newResponse storagePolicy]); + + m_handle->client()->willCacheResponse(m_handle, policy); + + if (static_cast<NSURLCacheStoragePolicy>(policy) != [newResponse storagePolicy]) + newResponse = [[[NSCachedURLResponse alloc] initWithResponse:[newResponse response] + data:[newResponse data] + userInfo:[newResponse userInfo] + storagePolicy:static_cast<NSURLCacheStoragePolicy>(policy)] autorelease]; + + return newResponse; +} + +@end + +#ifndef BUILDING_ON_TIGER + +WebCoreSynchronousLoaderClient::~WebCoreSynchronousLoaderClient() +{ + [m_response release]; + [m_data release]; + [m_error release]; +} + +void WebCoreSynchronousLoaderClient::willSendRequest(ResourceHandle* handle, ResourceRequest& request, const ResourceResponse& /*redirectResponse*/) +{ + // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. + if (!protocolHostAndPortAreEqual(handle->firstRequest().url(), request.url())) { + ASSERT(!m_error); + m_error = [[NSError alloc] initWithDomain:NSURLErrorDomain code:NSURLErrorBadServerResponse userInfo:nil]; + m_isDone = true; + request = 0; + return; + } +} + +bool WebCoreSynchronousLoaderClient::shouldUseCredentialStorage(ResourceHandle*) +{ + // FIXME: We should ask FrameLoaderClient whether using credential storage is globally forbidden. + return m_allowStoredCredentials; +} + +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +bool WebCoreSynchronousLoaderClient::canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&) +{ + // FIXME: We should ask FrameLoaderClient. + return true; +} +#endif + +void WebCoreSynchronousLoaderClient::didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) +{ + // FIXME: The user should be asked for credentials, as in async case. + [challenge.sender() continueWithoutCredentialForAuthenticationChallenge:challenge.nsURLAuthenticationChallenge()]; +} + +void WebCoreSynchronousLoaderClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) +{ + [m_response release]; + m_response = [response.nsURLResponse() copy]; +} + +void WebCoreSynchronousLoaderClient::didReceiveData(ResourceHandle*, const char* data, int length, int /*lengthReceived*/) +{ + if (!m_data) + m_data = [[NSMutableData alloc] init]; + [m_data appendBytes:data length:length]; +} + +void WebCoreSynchronousLoaderClient::didFinishLoading(ResourceHandle*, double) +{ + m_isDone = true; +} + +void WebCoreSynchronousLoaderClient::didFail(ResourceHandle*, const ResourceError& error) +{ + ASSERT(!m_error); + + m_error = [error copy]; + m_isDone = true; +} + +#endif // BUILDING_ON_TIGER + +#endif // !USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/mac/ResourceRequestMac.mm b/Source/WebCore/platform/network/mac/ResourceRequestMac.mm new file mode 100644 index 0000000..f0357e5 --- /dev/null +++ b/Source/WebCore/platform/network/mac/ResourceRequestMac.mm @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2006, 2007, 2008 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 COMPUTER, 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 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. + */ + +#import "config.h" +#import "ResourceRequest.h" + +#if !USE(CFNETWORK) + +#import "WebCoreSystemInterface.h" + +#import "FormDataStreamMac.h" + +#import <Foundation/Foundation.h> + +#ifdef BUILDING_ON_TIGER +typedef unsigned NSUInteger; +#endif + +@interface NSURLRequest (WebCoreContentDispositionEncoding) +- (NSArray *)contentDispositionEncodingFallbackArray; +@end + +@interface NSMutableURLRequest (WebCoreContentDispositionEncoding) +- (void)setContentDispositionEncodingFallbackArray:(NSArray *)theEncodingFallbackArray; +@end + +namespace WebCore { + +NSURLRequest* ResourceRequest::nsURLRequest() const +{ + updatePlatformRequest(); + + return [[m_nsRequest.get() retain] autorelease]; +} + +void ResourceRequest::doUpdateResourceRequest() +{ + m_url = [m_nsRequest.get() URL]; + m_cachePolicy = (ResourceRequestCachePolicy)[m_nsRequest.get() cachePolicy]; + m_timeoutInterval = [m_nsRequest.get() timeoutInterval]; + m_firstPartyForCookies = [m_nsRequest.get() mainDocumentURL]; + + if (NSString* method = [m_nsRequest.get() HTTPMethod]) + m_httpMethod = method; + m_allowCookies = [m_nsRequest.get() HTTPShouldHandleCookies]; + + NSDictionary *headers = [m_nsRequest.get() allHTTPHeaderFields]; + NSEnumerator *e = [headers keyEnumerator]; + NSString *name; + m_httpHeaderFields.clear(); + while ((name = [e nextObject])) + m_httpHeaderFields.set(name, [headers objectForKey:name]); + + // The below check can be removed once we require a version of Foundation with -[NSURLRequest contentDispositionEncodingFallbackArray] method. + static bool supportsContentDispositionEncodingFallbackArray = [NSURLRequest instancesRespondToSelector:@selector(contentDispositionEncodingFallbackArray)]; + if (supportsContentDispositionEncodingFallbackArray) { + m_responseContentDispositionEncodingFallbackArray.clear(); + NSArray *encodingFallbacks = [m_nsRequest.get() contentDispositionEncodingFallbackArray]; + NSUInteger count = [encodingFallbacks count]; + for (NSUInteger i = 0; i < count; ++i) { + CFStringEncoding encoding = CFStringConvertNSStringEncodingToEncoding([(NSNumber *)[encodingFallbacks objectAtIndex:i] unsignedLongValue]); + if (encoding != kCFStringEncodingInvalidId) + m_responseContentDispositionEncodingFallbackArray.append(CFStringConvertEncodingToIANACharSetName(encoding)); + } + } + + if (NSData* bodyData = [m_nsRequest.get() HTTPBody]) + m_httpBody = FormData::create([bodyData bytes], [bodyData length]); + else if (NSInputStream* bodyStream = [m_nsRequest.get() HTTPBodyStream]) + if (FormData* formData = httpBodyFromStream(bodyStream)) + m_httpBody = formData; +} + +void ResourceRequest::doUpdatePlatformRequest() +{ + if (isNull()) { + m_nsRequest = nil; + return; + } + + NSMutableURLRequest* nsRequest = [m_nsRequest.get() mutableCopy]; + + if (nsRequest) + [nsRequest setURL:url()]; + else + nsRequest = [[NSMutableURLRequest alloc] initWithURL:url()]; + +#ifdef BUILDING_ON_TIGER + wkSupportsMultipartXMixedReplace(nsRequest); +#endif + + [nsRequest setCachePolicy:(NSURLRequestCachePolicy)cachePolicy()]; + if (timeoutInterval() != unspecifiedTimeoutInterval) + [nsRequest setTimeoutInterval:timeoutInterval()]; + [nsRequest setMainDocumentURL:firstPartyForCookies()]; + if (!httpMethod().isEmpty()) + [nsRequest setHTTPMethod:httpMethod()]; + [nsRequest setHTTPShouldHandleCookies:allowCookies()]; + + // Cannot just use setAllHTTPHeaderFields here, because it does not remove headers. + NSArray *oldHeaderFieldNames = [[nsRequest allHTTPHeaderFields] allKeys]; + for (unsigned i = [oldHeaderFieldNames count]; i != 0; --i) + [nsRequest setValue:nil forHTTPHeaderField:[oldHeaderFieldNames objectAtIndex:i - 1]]; + HTTPHeaderMap::const_iterator end = httpHeaderFields().end(); + for (HTTPHeaderMap::const_iterator it = httpHeaderFields().begin(); it != end; ++it) + [nsRequest setValue:it->second forHTTPHeaderField:it->first]; + + // The below check can be removed once we require a version of Foundation with -[NSMutableURLRequest setContentDispositionEncodingFallbackArray:] method. + static bool supportsContentDispositionEncodingFallbackArray = [NSMutableURLRequest instancesRespondToSelector:@selector(setContentDispositionEncodingFallbackArray:)]; + if (supportsContentDispositionEncodingFallbackArray) { + NSMutableArray *encodingFallbacks = [NSMutableArray array]; + unsigned count = m_responseContentDispositionEncodingFallbackArray.size(); + for (unsigned i = 0; i != count; ++i) { + CFStringRef encodingName = m_responseContentDispositionEncodingFallbackArray[i].createCFString(); + unsigned long nsEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(encodingName)); + CFRelease(encodingName); + if (nsEncoding != kCFStringEncodingInvalidId) + [encodingFallbacks addObject:[NSNumber numberWithUnsignedLong:nsEncoding]]; + } + [nsRequest setContentDispositionEncodingFallbackArray:encodingFallbacks]; + } + + RefPtr<FormData> formData = httpBody(); + if (formData && !formData->isEmpty()) + WebCore::setHTTPBody(nsRequest, formData); + + m_nsRequest.adoptNS(nsRequest); +} + +void ResourceRequest::applyWebArchiveHackForMail() +{ + // Hack because Mail checks for this property to detect data / archive loads + [NSURLProtocol setProperty:@"" forKey:@"WebDataRequest" inRequest:(NSMutableURLRequest *)nsURLRequest()]; +} + +unsigned initializeMaximumHTTPConnectionCountPerHost() +{ + static const unsigned preferredConnectionCount = 6; + return wkInitializeMaximumHTTPConnectionCountPerHost(preferredConnectionCount); +} + +} // namespace WebCore + +#endif // !USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/mac/ResourceResponseMac.mm b/Source/WebCore/platform/network/mac/ResourceResponseMac.mm new file mode 100644 index 0000000..58c3641 --- /dev/null +++ b/Source/WebCore/platform/network/mac/ResourceResponseMac.mm @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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. + */ + +#import "config.h" +#import "ResourceResponse.h" + +#if !USE(CFNETWORK) + +#import "HTTPParsers.h" +#import "WebCoreURLResponse.h" +#import "WebCoreSystemInterface.h" +#import <Foundation/Foundation.h> +#import <wtf/StdLibExtras.h> +#import <limits> + +@interface NSURLResponse (FoundationSecretsWebCoreKnowsAbout) +- (NSTimeInterval)_calculatedExpiration; +@end + +#ifdef BUILDING_ON_TIGER +typedef int NSInteger; +#endif + +namespace WebCore { + +NSURLResponse *ResourceResponse::nsURLResponse() const +{ + if (!m_nsResponse && !m_isNull) { + // Work around a mistake in the NSURLResponse class. + // The init function takes an NSInteger, even though the accessor returns a long long. + // For values that won't fit in an NSInteger, pass -1 instead. + NSInteger expectedContentLength; + if (m_expectedContentLength < 0 || m_expectedContentLength > std::numeric_limits<NSInteger>::max()) + expectedContentLength = -1; + else + expectedContentLength = static_cast<NSInteger>(m_expectedContentLength); + const_cast<ResourceResponse*>(this)->m_nsResponse.adoptNS([[NSURLResponse alloc] initWithURL:m_url MIMEType:m_mimeType expectedContentLength:expectedContentLength textEncodingName:m_textEncodingName]); + } + return m_nsResponse.get(); +} + +void ResourceResponse::platformLazyInit() +{ + if (m_isUpToDate) + return; + m_isUpToDate = true; + + if (m_isNull) { + ASSERT(!m_nsResponse); + return; + } + + m_url = [m_nsResponse.get() URL]; + m_mimeType = [m_nsResponse.get() MIMEType]; + m_expectedContentLength = [m_nsResponse.get() expectedContentLength]; + m_textEncodingName = [m_nsResponse.get() textEncodingName]; + + // Workaround for <rdar://problem/8757088>, can be removed once that is fixed. + unsigned textEncodingNameLength = m_textEncodingName.length(); + if (textEncodingNameLength >= 2 && m_textEncodingName[0U] == '"' && m_textEncodingName[textEncodingNameLength - 1] == '"') + m_textEncodingName = m_textEncodingName.substring(1, textEncodingNameLength - 2); + + m_suggestedFilename = [m_nsResponse.get() suggestedFilename]; + + if ([m_nsResponse.get() isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)m_nsResponse.get(); + + m_httpStatusCode = [httpResponse statusCode]; + + RetainPtr<NSString> httpStatusLine(AdoptNS, wkCopyNSURLResponseStatusLine(m_nsResponse.get())); + if (httpStatusLine) + m_httpStatusText = extractReasonPhraseFromHTTPStatusLine(httpStatusLine.get()); + else + m_httpStatusText = "OK"; + + NSDictionary *headers = [httpResponse allHeaderFields]; + NSEnumerator *e = [headers keyEnumerator]; + while (NSString *name = [e nextObject]) + m_httpHeaderFields.set(name, [headers objectForKey:name]); + } else + m_httpStatusCode = 0; +} + +bool ResourceResponse::platformCompare(const ResourceResponse& a, const ResourceResponse& b) +{ + return a.nsURLResponse() == b.nsURLResponse(); +} + +} // namespace WebCore + +#endif // !USE(CFNETWORK) diff --git a/Source/WebCore/platform/network/mac/WebCoreURLResponse.h b/Source/WebCore/platform/network/mac/WebCoreURLResponse.h new file mode 100644 index 0000000..8d43a21 --- /dev/null +++ b/Source/WebCore/platform/network/mac/WebCoreURLResponse.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008, 2009 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 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. + */ + +@interface NSURLResponse (WebCoreURLResponse) +-(void)adjustMIMETypeIfNecessary; +@end + +@interface NSURLResponse (Details) +- (void)_setMIMEType:(NSString *)type; +@end diff --git a/Source/WebCore/platform/network/mac/WebCoreURLResponse.mm b/Source/WebCore/platform/network/mac/WebCoreURLResponse.mm new file mode 100644 index 0000000..e287e5f --- /dev/null +++ b/Source/WebCore/platform/network/mac/WebCoreURLResponse.mm @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2008, 2009 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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 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. + */ + +#import "config.h" +#import "WebCoreURLResponse.h" + +#import "MIMETypeRegistry.h" +#import <objc/objc-class.h> +#import <wtf/Assertions.h> +#import <wtf/RetainPtr.h> + +#ifndef BUILDING_ON_TIGER +// <rdar://problem/5321972> Plain text document from HTTP server detected as application/octet-stream +// When we sniff a resource as application/octet-stream but the http response headers had "text/plain", +// we have a hard decision to make about which of the two generic MIME types to go with. +// When the URL's extension is a known binary type, we'll go with application/octet-stream. +// Otherwise, we'll trust the server. +static NSSet *createBinaryExtensionsSet() +{ + return [[NSSet alloc] initWithObjects: + @"3g2", + @"3gp", + @"ai", + @"aif", + @"aifc", + @"aiff", + @"au", + @"avi", + @"bcpio", + @"bin", + @"bmp", + @"boz", + @"bpk", + @"bz", + @"bz2", + @"chm", + @"class", + @"com", + @"cpio", + @"dcr", + @"dir", + @"dist", + @"distz", + @"dll", + @"dmg", + @"dms", + @"doc", + @"dot", + @"dump", + @"dv", + @"dvi", + @"dxr", + @"elc", + @"eot", + @"eps", + @"exe", + @"fgd", + @"gif", + @"gtar", + @"h261", + @"h263", + @"h264", + @"ico", + @"ims", + @"indd", + @"iso", + @"jp2", + @"jpe", + @"jpeg", + @"jpg", + @"jpgm", + @"jpgv", + @"jpm", + @"kar", + @"kmz", + @"lha", + @"lrm", + @"lzh", + @"m1v", + @"m2a", + @"m2v", + @"m3a", + @"m3u", + @"m4a", + @"m4p", + @"m4v", + @"mdb", + @"mid", + @"midi", + @"mj2", + @"mjp2", + @"mov", + @"movie", + @"mp2", + @"mp2a", + @"mp3", + @"mp4", + @"mp4a", + @"mp4s", + @"mp4v", + @"mpe", + @"mpeg", + @"mpg", + @"mpg4", + @"mpga", + @"mpp", + @"mpt", + @"msi", + @"ogg", + @"otf", + @"pct", + @"pdf", + @"pfa", + @"pfb", + @"pic", + @"pict", + @"pkg", + @"png", + @"pot", + @"pps", + @"ppt", + @"ps", + @"psd", + @"qt", + @"qti", + @"qtif", + @"qwd", + @"qwt", + @"qxb", + @"qxd", + @"qxl", + @"qxp", + @"qxt", + @"ra", + @"ram", + @"rm", + @"rmi", + @"rmp", + @"scpt", + @"sit", + @"sitx", + @"snd", + @"so", + @"swf", + @"tar", + @"tif", + @"tiff", + @"ttf", + @"wav", + @"wcm", + @"wdb", + @"wks", + @"wm", + @"wma", + @"wmd", + @"wmf", + @"wmv", + @"wmx", + @"wmz", + @"wpd", + @"wpl", + @"wps", + @"wvx", + @"xla", + @"xlc", + @"xlm", + @"xls", + @"xlt", + @"xlw", + @"xps", + @"zip", + nil + ]; +} +#endif + +// <rdar://problem/7007389> CoreTypes UTI map is missing 100+ file extensions that GateKeeper knew about +// When we disabled content sniffing for file URLs we caused problems with these 100+ extensions that CoreTypes +// doesn't know about. +// If CoreTypes is ever brought up to speed we can remove this table and associated code. +static NSDictionary *createExtensionToMIMETypeMap() +{ + return [[NSDictionary alloc] initWithObjectsAndKeys: + @"application/postscript", @"ai", + @"text/plain", @"asc", + @"application/x-bcpio", @"bcpio", + @"image/bmp", @"bmp", + @"application/x-netcdf", @"cdf", + @"application/octet-stream", @"class", + @"application/x-gzip", @"cpgz", + @"application/x-cpio", @"cpio", + @"application/mac-compactpro", @"cpt", + @"application/x-csh", @"csh", + @"text/css", @"css", + @"application/x-director", @"dcr", + @"application/x-director", @"dir", + @"application/x-diskcopy", @"dmg", + @"application/octet-stream", @"dms", + @"application/x-dvi", @"dvi", + @"application/x-director", @"dxr", + @"application/postscript", @"eps", + @"text/x-setext", @"etx", + @"application/andrew-inset", @"ez", + @"application/vnd.fdf", @"fdf", + @"application/octet-stream", @"fla", + @"application/x-filemaker", @"fp", + @"application/x-filemaker", @"fp2", + @"application/x-filemaker", @"fp3", + @"application/x-filemaker", @"fp4", + @"application/x-filemaker", @"fp5", + @"application/x-filemaker", @"fp6", + @"application/x-hdf", @"hdf", + @"x-conference/x-cooltalk", @"ice", + @"image/x-icon", @"ico", + @"text/calendar", @"ics", + @"image/ief", @"ief", + @"model/iges", @"iges", + @"model/iges", @"igs", + @"application/octet-stream", @"iso", + @"text/html", @"jhtml", + @"application/x-latex", @"latex", + @"application/octet-stream", @"lha", + @"application/octet-stream", @"lzh", + @"audio/x-mpegurl", @"m3u", + @"audio/x-m4p", @"m4p", + @"image/x-macpaint", @"mac", + @"application/x-troff-man", @"man", + @"application/x-troff-me", @"me", + @"model/mesh", @"mesh", + @"application/vnd.mif", @"mif", + @"video/x-sgi-movie", @"movie", + @"audio/mpeg", @"mp2", + @"audio/mpeg", @"mpga", + @"application/x-troff-ms", @"ms", + @"model/mesh", @"msh", + @"video/vnd.mpegurl", @"mxu", + @"application/x-netcdf", @"nc", + @"application/oda", @"oda", + @"image/x-portable-bitmap", @"pbm", + @"image/x-pcx", @"pcx", + @"chemical/x-pdb", @"pdb", + @"image/x-portable-graymap", @"pgm", + @"application/x-chess-pgn", @"pgn", + @"audio/scpls", @"pls", + @"image/x-portable-anymap", @"pnm", + @"image/x-macpaint", @"pnt", + @"image/x-macpaint", @"pntg", + @"image/x-portable-pixmap", @"ppm", + @"image/x-cmu-raster", @"ras", + @"image/x-rgb", @"rgb", + @"application/x-troff", @"roff", + @"audio/x-pn-realaudio-plugin", @"rpm", + @"text/richtext", @"rtx", + @"text/sgml", @"sgm", + @"text/sgml", @"sgml", + @"application/x-sh", @"sh", + @"application/x-shar", @"shar", + @"model/mesh", @"silo", + @"application/x-koan", @"skd", + @"application/x-koan", @"skm", + @"application/x-koan", @"skp", + @"application/x-koan", @"skt", + @"application/x-diskcopy", @"smi", + @"application/octet-stream", @"so", + @"application/x-futuresplash", @"spl", + @"application/x-wais-source", @"src", + @"application/x-sv4cpio", @"sv4cpio", + @"application/x-sv4crc", @"sv4crc", + @"application/x-shockwave-flash", @"swf", + @"application/x-troff", @"t", + @"image/x-targa", @"targa", + @"application/x-tcl", @"tcl", + @"application/x-tex", @"tex", + @"application/x-texinfo", @"texi", + @"application/x-texinfo", @"texinfo", + @"application/x-gzip", @"tgz", + @"application/x-bittorrent", @"torrent", + @"application/x-troff", @"tr", + @"text/tab-separated-values", @"tsv", + @"application/x-ustar", @"ustar", + @"application/x-cdlink", @"vcd", + @"model/vrml", @"vrml", + @"image/vnd.wap.wbmp", @"wbmp", + @"application/vnd.wap.wbxml", @"wbxml", + @"application/x-webarchive", @"webarchive", + @"application/x-ms-wmd", @"wmd", + @"text/vnd.wap.wml", @"wml", + @"application/vnd.wap.wmlc", @"wmlc", + @"text/vnd.wap.wmlscript", @"wmls", + @"application/vnd.wap.wmlscriptc", @"wmlsc", + @"model/vrml", @"wrl", + @"application/vnd.adobe.xdp+xml", @"xdp", + @"application/vnd.adobe.xfd+xml", @"xfd", + @"application/vnd.adobe.xfdf", @"xfdf", + @"image/x-xpixmap", @"xpm", + @"text/xml", @"xsl", + @"image/x-xwindowdump", @"xwd", + @"chemical/x-xyz", @"xyz", + @"application/x-compress", @"z", + nil + ]; +} + +static RetainPtr<NSString> mimeTypeFromUTITree(CFStringRef uti) +{ + // Check if this UTI has a MIME type. + RetainPtr<CFStringRef> mimeType(AdoptCF, UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)); + if (mimeType) + return (NSString *)mimeType.get(); + + // If not, walk the ancestory of this UTI via its "ConformsTo" tags and return the first MIME type we find. + RetainPtr<CFDictionaryRef> decl(AdoptCF, UTTypeCopyDeclaration(uti)); + if (!decl) + return nil; + CFTypeRef value = CFDictionaryGetValue(decl.get(), kUTTypeConformsToKey); + if (!value) + return nil; + CFTypeID typeID = CFGetTypeID(value); + + if (typeID == CFStringGetTypeID()) + return mimeTypeFromUTITree((CFStringRef)value); + + if (typeID == CFArrayGetTypeID()) { + CFArrayRef newTypes = (CFArrayRef)value; + CFIndex count = CFArrayGetCount(newTypes); + for (CFIndex i = 0; i < count; ++i) { + CFTypeRef object = CFArrayGetValueAtIndex(newTypes, i); + if (CFGetTypeID(object) != CFStringGetTypeID()) + continue; + + if (RetainPtr<NSString> mimeType = mimeTypeFromUTITree((CFStringRef)object)) + return mimeType; + } + } + + return nil; +} + +@implementation NSURLResponse (WebCoreURLResponse) + +-(void)adjustMIMETypeIfNecessary +{ + RetainPtr<NSString> result = [self MIMEType]; + RetainPtr<NSString> originalResult = result; + +#ifdef BUILDING_ON_TIGER + // When content sniffing is disabled, Tiger's CFNetwork automatically returns application/octet-stream for certain + // extensions even when scouring the UTI maps would end up with a better result, so we'll give a chance for that to happen. + if ([[self URL] isFileURL] && [result.get() caseInsensitiveCompare:@"application/octet-stream"] == NSOrderedSame) + result = nil; +#endif + + if (!result) { + NSURL *url = [self URL]; + if ([url isFileURL]) { + if (NSString *extension = [[url path] pathExtension]) { + // <rdar://problem/7007389> CoreTypes UTI map is missing 100+ file extensions that GateKeeper knew about + // When this radar is resolved, we can remove this file:// url specific code. + static NSDictionary *extensionMap = createExtensionToMIMETypeMap(); + result = [extensionMap objectForKey:[extension lowercaseString]]; + + if (!result) { + // If the Gatekeeper-based map doesn't have a MIME type, we'll try to figure out what it should be by + // looking up the file extension in the UTI maps. + RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)extension, 0)); + result = mimeTypeFromUTITree(uti.get()); + } + } + } + } + + if (!result) { + static NSString *defaultMIMETypeString = [(NSString *)WebCore::defaultMIMEType() retain]; + result = defaultMIMETypeString; + } + +#ifndef BUILDING_ON_TIGER + // <rdar://problem/5321972> Plain text document from HTTP server detected as application/octet-stream + // Make the best guess when deciding between "generic binary" and "generic text" using a table of known binary MIME types. + if ([result.get() isEqualToString:@"application/octet-stream"] && [self respondsToSelector:@selector(allHeaderFields)] && [[[self performSelector:@selector(allHeaderFields)] objectForKey:@"Content-Type"] hasPrefix:@"text/plain"]) { + static NSSet *binaryExtensions = createBinaryExtensionsSet(); + if (![binaryExtensions containsObject:[[[self suggestedFilename] pathExtension] lowercaseString]]) + result = @"text/plain"; + } + +#endif + +#ifdef BUILDING_ON_LEOPARD + // Workaround for <rdar://problem/5539824> + if ([result.get() isEqualToString:@"text/xml"]) + result = @"application/xml"; +#endif + + if (result != originalResult) + [self _setMIMEType:result.get()]; +} + +@end diff --git a/Source/WebCore/platform/network/qt/AuthenticationChallenge.h b/Source/WebCore/platform/network/qt/AuthenticationChallenge.h new file mode 100644 index 0000000..ebbc0cd --- /dev/null +++ b/Source/WebCore/platform/network/qt/AuthenticationChallenge.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 AuthenticationChallenge_h +#define AuthenticationChallenge_h + +#include "AuthenticationChallengeBase.h" + +namespace WebCore { + +class AuthenticationClient; + +class AuthenticationChallenge : public AuthenticationChallengeBase { +public: + AuthenticationChallenge() + { + } + + AuthenticationChallenge(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error) + : AuthenticationChallengeBase(protectionSpace, proposedCredential, previousFailureCount, response, error) + { + } + + AuthenticationClient* authenticationClient() const { return 0; } // FIXME: Implement! + +}; + +} + +#endif diff --git a/Source/WebCore/platform/network/qt/CredentialStorageQt.cpp b/Source/WebCore/platform/network/qt/CredentialStorageQt.cpp new file mode 100644 index 0000000..54ccad0 --- /dev/null +++ b/Source/WebCore/platform/network/qt/CredentialStorageQt.cpp @@ -0,0 +1,38 @@ +/* + * 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 "CredentialStorage.h" + +#include "Credential.h" + +namespace WebCore { + +Credential CredentialStorage::getFromPersistentStorage(const ProtectionSpace&) +{ + return Credential(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/qt/DnsPrefetchHelper.cpp b/Source/WebCore/platform/network/qt/DnsPrefetchHelper.cpp new file mode 100644 index 0000000..d017b90 --- /dev/null +++ b/Source/WebCore/platform/network/qt/DnsPrefetchHelper.cpp @@ -0,0 +1,35 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + 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 "DnsPrefetchHelper.h" + +#include "PlatformString.h" + +namespace WebCore { +// this is called on mouse over a href and on page loading +void prefetchDNS(const String& hostname) +{ + if (QWebSettings::globalSettings()->testAttribute(QWebSettings::DnsPrefetchEnabled)) { + static DnsPrefetchHelper dnsPrefetchHelper; + dnsPrefetchHelper.lookup(QString(hostname)); + } +} + +} diff --git a/Source/WebCore/platform/network/qt/DnsPrefetchHelper.h b/Source/WebCore/platform/network/qt/DnsPrefetchHelper.h new file mode 100644 index 0000000..892a3fb --- /dev/null +++ b/Source/WebCore/platform/network/qt/DnsPrefetchHelper.h @@ -0,0 +1,86 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + 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 DnsPrefetchHelper_h +#define DnsPrefetchHelper_h + +#include <QObject> +#include <QCache> +#include <QHostInfo> +#include <QSet> +#include <QString> +#include <QTime> +#include "qwebsettings.h" + +namespace WebCore { + + class DnsPrefetchHelper : public QObject { + Q_OBJECT + public: + DnsPrefetchHelper() : QObject(), currentLookups(0) {}; + + public slots: + void lookup(QString hostname) + { + if (hostname.isEmpty()) + return; // this actually happens + if (currentLookups >= 10) + return; // do not launch more than 10 lookups at the same time + +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 3) + currentLookups++; + QHostInfo::lookupHost(hostname, this, SLOT(lookedUp(QHostInfo))); +#else + // This code is only needed for Qt versions that do not have + // the small Qt DNS cache yet. + + QTime* entryTime = lookupCache.object(hostname); + if (entryTime && entryTime->elapsed() > 300*1000) { + // delete knowledge about lookup if it is already 300 seconds old + lookupCache.remove(hostname); + } else if (!entryTime) { + // not in cache yet, can look it up + QTime *tmpTime = new QTime(); + *tmpTime = QTime::currentTime(); + lookupCache.insert(hostname, tmpTime); + currentLookups++; + QHostInfo::lookupHost(hostname, this, SLOT(lookedUp(QHostInfo))); + } +#endif + } + + void lookedUp(const QHostInfo&) + { + // we do not cache the result, we throw it away. + // we currently rely on the OS to cache the results. If it does not do that + // then at least the ISP nameserver did it. + // Since Qt 4.6.3, Qt also has a small DNS cache. + currentLookups--; + } + + protected: +#if QT_VERSION < QT_VERSION_CHECK(4, 6, 3) + QCache<QString, QTime> lookupCache; // 100 entries +#endif + int currentLookups; + }; + + +} + +#endif // DnsPrefetchHelper_h diff --git a/Source/WebCore/platform/network/qt/NetworkStateNotifierPrivate.h b/Source/WebCore/platform/network/qt/NetworkStateNotifierPrivate.h new file mode 100644 index 0000000..766dc90 --- /dev/null +++ b/Source/WebCore/platform/network/qt/NetworkStateNotifierPrivate.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + 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 NetworkStateNotifierPrivate_h +#define NetworkStateNotifierPrivate_h + +#include <QObject> + +#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) +namespace QtMobility { +class QNetworkConfigurationManager; +} +#else +QT_BEGIN_NAMESPACE +class QNetworkConfigurationManager; +QT_END_NAMESPACE +#endif + +namespace WebCore { + +class NetworkStateNotifier; + +class NetworkStateNotifierPrivate : public QObject { + Q_OBJECT +public: + NetworkStateNotifierPrivate(NetworkStateNotifier* notifier); + ~NetworkStateNotifierPrivate(); +public slots: + void onlineStateChanged(bool); + void networkAccessPermissionChanged(bool); + +public: +#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) + QtMobility::QNetworkConfigurationManager* m_configurationManager; +#else + QNetworkConfigurationManager* m_configurationManager; +#endif + bool m_online; + bool m_networkAccessAllowed; + NetworkStateNotifier* m_notifier; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/qt/NetworkStateNotifierQt.cpp b/Source/WebCore/platform/network/qt/NetworkStateNotifierQt.cpp new file mode 100644 index 0000000..959e74a --- /dev/null +++ b/Source/WebCore/platform/network/qt/NetworkStateNotifierQt.cpp @@ -0,0 +1,96 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + 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 "NetworkStateNotifier.h" + +#if PLATFORM(QT) && ENABLE(QT_BEARER) + +#include "NetworkStateNotifierPrivate.h" +#include "qnetworkconfigmanager.h" + +#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) +using namespace QtMobility; +#endif + +namespace WebCore { + +NetworkStateNotifierPrivate::NetworkStateNotifierPrivate(NetworkStateNotifier* notifier) + : m_configurationManager(new QNetworkConfigurationManager()) + , m_online(m_configurationManager->isOnline()) + , m_networkAccessAllowed(true) + , m_notifier(notifier) +{ + Q_ASSERT(notifier); + connect(m_configurationManager, SIGNAL(onlineStateChanged(bool)), this, SLOT(onlineStateChanged(bool))); +} + +void NetworkStateNotifierPrivate::onlineStateChanged(bool isOnline) +{ + if (m_online == isOnline) + return; + + m_online = isOnline; + if (m_networkAccessAllowed) + m_notifier->updateState(); +} + +void NetworkStateNotifierPrivate::networkAccessPermissionChanged(bool isAllowed) +{ + if (isAllowed == m_networkAccessAllowed) + return; + + m_networkAccessAllowed = isAllowed; + if (m_online) + m_notifier->updateState(); +} + +NetworkStateNotifierPrivate::~NetworkStateNotifierPrivate() +{ + delete m_configurationManager; +} + +void NetworkStateNotifier::updateState() +{ + if (m_isOnLine == (p->m_online && p->m_networkAccessAllowed)) + return; + + m_isOnLine = p->m_online && p->m_networkAccessAllowed; + if (m_networkStateChangedFunction) + m_networkStateChangedFunction(); +} + +NetworkStateNotifier::NetworkStateNotifier() + : m_isOnLine(true) + , m_networkStateChangedFunction(0) +{ + p = new NetworkStateNotifierPrivate(this); + m_isOnLine = p->m_online && p->m_networkAccessAllowed; +} + +void NetworkStateNotifier::setNetworkAccessAllowed(bool isAllowed) +{ + p->networkAccessPermissionChanged(isAllowed); +} + +} // namespace WebCore + +#endif + +#include "moc_NetworkStateNotifierPrivate.cpp" diff --git a/Source/WebCore/platform/network/qt/ProxyServerQt.cpp b/Source/WebCore/platform/network/qt/ProxyServerQt.cpp new file mode 100644 index 0000000..0bf3687 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/qt/QNetworkReplyHandler.cpp b/Source/WebCore/platform/network/qt/QNetworkReplyHandler.cpp new file mode 100644 index 0000000..98fd68d --- /dev/null +++ b/Source/WebCore/platform/network/qt/QNetworkReplyHandler.cpp @@ -0,0 +1,613 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2007 Staikos Computing Services Inc. <info@staikos.net> + Copyright (C) 2008 Holger Hans Peter Freyther + + 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 "QNetworkReplyHandler.h" + +#include "HTTPParsers.h" +#include "MIMETypeRegistry.h" +#include "QtNAMThreadSafeProxy.h" +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "ResourceHandleInternal.h" +#include "ResourceResponse.h" +#include "ResourceRequest.h" +#include <QDateTime> +#include <QFile> +#include <QFileInfo> +#include <QNetworkReply> +#include <QNetworkCookie> +#include <qwebframe.h> +#include <qwebpage.h> + +#include <wtf/text/CString.h> + +#include <QDebug> +#include <QCoreApplication> + +// What type of connection should be used for the signals of the +// QNetworkReply? This depends on if Qt has a bugfix for this or not. +// It is fixed in Qt 4.6.3. See https://bugs.webkit.org/show_bug.cgi?id=32113 +// and https://bugs.webkit.org/show_bug.cgi?id=36755 +#if QT_VERSION > QT_VERSION_CHECK(4, 6, 2) +#define SIGNAL_CONN Qt::AutoConnection +#else +#define SIGNAL_CONN Qt::QueuedConnection +#endif + +static const int gMaxRecursionLimit = 10; + +namespace WebCore { + +// Take a deep copy of the FormDataElement +FormDataIODevice::FormDataIODevice(FormData* data, QObject* parent) + : QIODevice(parent) + , 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() +{ + 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) + m_currentFile->close(); + m_currentDelta = 0; + + m_formElements.remove(0); + + if (m_formElements.isEmpty() || m_formElements[0].m_type == FormDataElement::data) + return; + + openFileForCurrentElement(); +} + +void FormDataIODevice::openFileForCurrentElement() +{ + if (!m_currentFile) + m_currentFile = new QFile; + + m_currentFile->setFileName(m_formElements[0].m_filename); + m_currentFile->open(QFile::ReadOnly); +} + +// m_formElements[0] is the current item. If the destination buffer is +// big enough we are going to read from more than one FormDataElement +qint64 FormDataIODevice::readData(char* destination, qint64 size) +{ + if (m_formElements.isEmpty()) + return -1; + + qint64 copied = 0; + while (copied < size && !m_formElements.isEmpty()) { + const FormDataElement& element = m_formElements[0]; + const qint64 available = size-copied; + + if (element.m_type == FormDataElement::data) { + const qint64 toCopy = qMin<qint64>(available, element.m_data.size() - m_currentDelta); + memcpy(destination+copied, element.m_data.data()+m_currentDelta, toCopy); + m_currentDelta += toCopy; + copied += toCopy; + + if (m_currentDelta == element.m_data.size()) + moveToNextElement(); + } else { + const QByteArray data = m_currentFile->read(available); + memcpy(destination+copied, data.constData(), data.size()); + copied += data.size(); + + if (m_currentFile->atEnd() || !m_currentFile->isOpen()) + moveToNextElement(); + } + } + + return copied; +} + +qint64 FormDataIODevice::writeData(const char*, qint64) +{ + return -1; +} + +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) + , m_resourceHandle(handle) + , m_redirected(false) + , m_responseSent(false) + , m_responseContainsData(false) + , m_loadMode(loadMode) + , m_shouldStart(true) + , m_shouldFinish(false) + , m_shouldSendResponse(false) + , m_shouldForwardData(false) + , m_redirectionTries(gMaxRecursionLimit) +{ + // Make this a direct function call once we require 4.6.1+. + connect(this, SIGNAL(processQueuedItems()), this, SLOT(sendQueuedItems()), SIGNAL_CONN); + + const ResourceRequest &r = m_resourceHandle->firstRequest(); + + if (r.httpMethod() == "GET") + m_method = QNetworkAccessManager::GetOperation; + else if (r.httpMethod() == "HEAD") + m_method = QNetworkAccessManager::HeadOperation; + else if (r.httpMethod() == "POST") + m_method = QNetworkAccessManager::PostOperation; + else if (r.httpMethod() == "PUT") + m_method = QNetworkAccessManager::PutOperation; + else if (r.httpMethod() == "DELETE") + m_method = QNetworkAccessManager::DeleteOperation; +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + else + m_method = QNetworkAccessManager::CustomOperation; +#else + else + m_method = QNetworkAccessManager::UnknownOperation; +#endif + + QObject* originatingObject = 0; + if (m_resourceHandle->getInternal()->m_context) + originatingObject = m_resourceHandle->getInternal()->m_context->originatingObject(); + + m_request = r.toNetworkRequest(originatingObject); + + if (m_loadMode == LoadNormal) + start(); +} + +QNetworkReplyHandler::~QNetworkReplyHandler() +{ + if (m_reply) + m_reply->deleteLater(); +} + +void QNetworkReplyHandler::setLoadMode(LoadMode mode) +{ + // https://bugs.webkit.org/show_bug.cgi?id=26556 + // We cannot call sendQueuedItems() from here, because the signal that + // caused us to get into deferred mode, might not be processed yet. + switch (mode) { + case LoadNormal: + m_loadMode = LoadResuming; + emit processQueuedItems(); + + // sendQueuedItems() may cause m_reply to be set to 0 due to the finish() call causing + // the ResourceHandle instance that owns this QNetworkReplyHandler to be destroyed. + if (m_reply) { + // Restart forwarding only after processQueuedItems to make sure + // our buffered data was handled before any incoming data. + m_reply->setForwardingDefered(false); + } + break; + case LoadDeferred: + if (m_reply) { + m_loadMode = LoadDeferred; + m_reply->setForwardingDefered(true); + } + break; + case LoadResuming: + Q_ASSERT(0); // should never happen + break; + }; +} + +void QNetworkReplyHandler::abort() +{ + m_resourceHandle = 0; + if (m_reply) { + QtNetworkReplyThreadSafeProxy* reply = release(); + reply->abort(); + reply->deleteLater(); + } + deleteLater(); +} + +QtNetworkReplyThreadSafeProxy* QNetworkReplyHandler::release() +{ + QtNetworkReplyThreadSafeProxy* reply = m_reply; + if (m_reply) { + disconnect(m_reply, 0, this, 0); + // We have queued connections to the QNetworkReply. Make sure any + // posted meta call events that were the result of a signal emission + // don't reach the slots in our instance. + QCoreApplication::removePostedEvents(this, QEvent::MetaCall); + m_reply = 0; + } + return reply; +} + +static bool ignoreHttpError(QtNetworkReplyThreadSafeProxy* reply, bool receivedData) +{ + int httpStatusCode = reply->httpStatusCode(); + + if (httpStatusCode == 401 || httpStatusCode == 407) + return true; + + if (receivedData && (httpStatusCode >= 400 && httpStatusCode < 600)) + return true; + + return false; +} + +void QNetworkReplyHandler::finish() +{ + m_shouldFinish = (m_loadMode != LoadNormal); + if (m_shouldFinish) + return; + + if (!m_reply) + return; + + sendResponseIfNeeded(); + + if (!m_resourceHandle) + return; + ResourceHandleClient* client = m_resourceHandle->client(); + if (!client) { + m_reply->deleteLater(); + m_reply = 0; + return; + } + + if (!m_redirected) { + if (!m_reply->error() || ignoreHttpError(m_reply, m_responseContainsData)) + client->didFinishLoading(m_resourceHandle, 0); + else { + QUrl url = m_reply->url(); + int httpStatusCode = m_reply->httpStatusCode(); + + if (httpStatusCode) { + ResourceError error("HTTP", httpStatusCode, url.toString(), QString::fromAscii(m_reply->httpReasonPhrase())); + client->didFail(m_resourceHandle, error); + } else { + ResourceError error("QtNetwork", m_reply->error(), url.toString(), m_reply->errorString()); + client->didFail(m_resourceHandle, error); + } + } + if (m_reply) { + m_reply->deleteLater(); + m_reply = 0; + } + } else { + if (m_reply) { + m_reply->deleteLater(); + m_reply = 0; + } + resetState(); + start(); + } +} + +void QNetworkReplyHandler::sendResponseIfNeeded() +{ + m_shouldSendResponse = (m_loadMode != LoadNormal); + if (m_shouldSendResponse) + return; + + if (!m_reply) + return; + + if (m_reply->error() && !ignoreHttpError(m_reply, m_responseContainsData)) + return; + + if (m_responseSent || !m_resourceHandle) + return; + m_responseSent = true; + + ResourceHandleClient* client = m_resourceHandle->client(); + if (!client) + return; + + WTF::String contentType = m_reply->contentTypeHeader(); + WTF::String encoding = extractCharsetFromMediaType(contentType); + WTF::String mimeType = extractMIMETypeFromMediaType(contentType); + + if (mimeType.isEmpty()) { + // let's try to guess from the extension + mimeType = MIMETypeRegistry::getMIMETypeForPath(m_reply->url().path()); + } + + KURL url(m_reply->url()); + ResourceResponse response(url, mimeType.lower(), + m_reply->contentLengthHeader(), + encoding, String()); + + if (url.isLocalFile()) { + client->didReceiveResponse(m_resourceHandle, response); + return; + } + + // The status code is equal to 0 for protocols not in the HTTP family. + int statusCode = m_reply->httpStatusCode(); + + if (url.protocolInHTTPFamily()) { + String suggestedFilename = filenameFromHTTPContentDisposition(QString::fromAscii(m_reply->contentDispositionHeader())); + + if (!suggestedFilename.isEmpty()) + response.setSuggestedFilename(suggestedFilename); + else + response.setSuggestedFilename(url.lastPathComponent()); + + response.setHTTPStatusCode(statusCode); + response.setHTTPStatusText(m_reply->httpReasonPhrase().constData()); + + // Add remaining headers. + foreach (const QtNetworkReplyThreadSafeProxy::RawHeaderPair& pair, m_reply->rawHeaderPairs()) { + response.setHTTPHeaderField(QString::fromAscii(pair.first), QString::fromAscii(pair.second)); + } + } + + QUrl redirection = m_reply->redirectionTarget(); + if (redirection.isValid()) { + QUrl newUrl = m_reply->url().resolved(redirection); + + m_redirectionTries--; + if (m_redirectionTries == 0) { // 10 or more redirections to the same url is considered infinite recursion + ResourceError error(newUrl.host(), 400 /*bad request*/, + newUrl.toString(), + QCoreApplication::translate("QWebPage", "Redirection limit reached")); + client->didFail(m_resourceHandle, error); + return; + } + m_redirected = true; + + + // 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; + + 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")) + newRequest.clearHTTPReferrer(); + + client->willSendRequest(m_resourceHandle, newRequest, response); + if (!m_resourceHandle) // network error did cancel the request + return; + + QObject* originatingObject = 0; + if (m_resourceHandle->getInternal()->m_context) + originatingObject = m_resourceHandle->getInternal()->m_context->originatingObject(); + + m_request = newRequest.toNetworkRequest(originatingObject); + return; + } + + client->didReceiveResponse(m_resourceHandle, response); +} + +void QNetworkReplyHandler::forwardData(const QByteArray &data) +{ + m_shouldForwardData = (m_loadMode != LoadNormal); + if (m_shouldForwardData) { + m_bufferedData += data; + return; + } + + if (!data.isEmpty()) + m_responseContainsData = true; + + sendResponseIfNeeded(); + + // don't emit the "Document has moved here" type of HTML + if (m_redirected) + return; + + if (!m_resourceHandle) + return; + + ResourceHandleClient* client = m_resourceHandle->client(); + if (!client) + return; + + if (!data.isEmpty()) + client->didReceiveData(m_resourceHandle, data.constData(), data.length(), data.length() /*FixMe*/); +} + +void QNetworkReplyHandler::uploadProgress(qint64 bytesSent, qint64 bytesTotal) +{ + if (!m_resourceHandle) + return; + + ResourceHandleClient* client = m_resourceHandle->client(); + if (!client) + return; + + client->didSendData(m_resourceHandle, bytesSent, bytesTotal); +} + +void QNetworkReplyHandler::start() +{ + m_shouldStart = false; + + ResourceHandleInternal* d = m_resourceHandle->getInternal(); + + QNetworkAccessManager* manager = 0; + if (d->m_context) + manager = d->m_context->networkAccessManager(); + + if (!manager) + return; + + const QUrl url = m_request.url(); + const QString scheme = url.scheme(); + // Post requests on files and data don't really make sense, but for + // fast/forms/form-post-urlencoded.html and for fast/forms/button-state-restore.html + // we still need to retrieve the file/data, which means we map it to a Get instead. + if (m_method == QNetworkAccessManager::PostOperation + && (!url.toLocalFile().isEmpty() || url.scheme() == QLatin1String("data"))) + m_method = QNetworkAccessManager::GetOperation; + + m_reply = new QtNetworkReplyThreadSafeProxy(manager); + connect(m_reply, SIGNAL(finished()), this, SLOT(finish()), SIGNAL_CONN); + + // For http(s) we know that the headers are complete upon metaDataChanged() emission, so we + // can send the response as early as possible + if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) + connect(m_reply, SIGNAL(metaDataChanged()), this, SLOT(sendResponseIfNeeded()), SIGNAL_CONN); + + connect(m_reply, SIGNAL(dataReceived(const QByteArray&)), this, SLOT(forwardData(const QByteArray&)), SIGNAL_CONN); + + if (m_resourceHandle->firstRequest().reportUploadProgress()) + connect(m_reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64)), SIGNAL_CONN); + + switch (m_method) { + case QNetworkAccessManager::GetOperation: + m_reply->get(m_request); + break; + case QNetworkAccessManager::PostOperation: { + FormDataIODevice* postDevice = new FormDataIODevice(d->m_firstRequest.httpBody(), this); + // 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->post(m_request, postDevice); + break; + } + case QNetworkAccessManager::HeadOperation: + m_reply->head(m_request); + break; + case QNetworkAccessManager::PutOperation: { + FormDataIODevice* putDevice = new FormDataIODevice(d->m_firstRequest.httpBody(), this); + // 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->put(m_request, putDevice); + break; + } + case QNetworkAccessManager::DeleteOperation: { + m_reply->deleteResource(m_request); + break; + } +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + case QNetworkAccessManager::CustomOperation: + m_reply->sendCustomRequest(m_request, m_resourceHandle->firstRequest().httpMethod().latin1().data()); + break; +#endif + case QNetworkAccessManager::UnknownOperation: { + m_reply->deleteLater(); + m_reply = 0; + ResourceHandleClient* client = m_resourceHandle->client(); + if (client) { + ResourceError error(url.host(), 400 /*bad request*/, + url.toString(), + QCoreApplication::translate("QWebPage", "Bad HTTP request")); + client->didFail(m_resourceHandle, error); + } + return; + } + } +} + +void QNetworkReplyHandler::resetState() +{ + m_redirected = false; + m_responseSent = false; + m_responseContainsData = false; + m_shouldStart = true; + m_shouldFinish = false; + m_shouldSendResponse = false; + m_shouldForwardData = false; +} + +void QNetworkReplyHandler::sendQueuedItems() +{ + if (m_loadMode != LoadResuming) + return; + m_loadMode = LoadNormal; + + if (m_shouldStart) + start(); + + if (m_shouldSendResponse) + sendResponseIfNeeded(); + + if (m_shouldForwardData) { + forwardData(m_bufferedData); + m_bufferedData.clear(); + } + + if (m_shouldFinish) + finish(); +} + +} + +#include "moc_QNetworkReplyHandler.cpp" diff --git a/Source/WebCore/platform/network/qt/QNetworkReplyHandler.h b/Source/WebCore/platform/network/qt/QNetworkReplyHandler.h new file mode 100644 index 0000000..11638b3 --- /dev/null +++ b/Source/WebCore/platform/network/qt/QNetworkReplyHandler.h @@ -0,0 +1,124 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + 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 QNetworkReplyHandler_h +#define QNetworkReplyHandler_h + +#include <QObject> + +#include <QNetworkRequest> +#include <QNetworkAccessManager> +#include <QNetworkReply> + +#include "FormData.h" + +QT_BEGIN_NAMESPACE +class QFile; +class QNetworkReply; +QT_END_NAMESPACE + +namespace WebCore { + +class ResourceHandle; +class QtNetworkReplyThreadSafeProxy; + +class QNetworkReplyHandler : public QObject +{ + Q_OBJECT +public: + enum LoadMode { + LoadNormal, + LoadDeferred, + LoadResuming + }; + + QNetworkReplyHandler(ResourceHandle *handle, LoadMode); + ~QNetworkReplyHandler(); + void setLoadMode(LoadMode); + + void abort(); + + QtNetworkReplyThreadSafeProxy* release(); + +signals: + void processQueuedItems(); + +private slots: + void finish(); + void sendResponseIfNeeded(); + void forwardData(const QByteArray &data); + void sendQueuedItems(); + void uploadProgress(qint64 bytesSent, qint64 bytesTotal); + +private: + void start(); + void resetState(); + String httpMethod() const; + + QtNetworkReplyThreadSafeProxy* m_reply; + ResourceHandle* m_resourceHandle; + bool m_redirected; + bool m_responseSent; + bool m_responseContainsData; + LoadMode m_loadMode; + QNetworkAccessManager::Operation m_method; + QNetworkRequest m_request; + + // defer state holding + bool m_shouldStart; + bool m_shouldFinish; + bool m_shouldSendResponse; + bool m_shouldForwardData; + int m_redirectionTries; + QByteArray m_bufferedData; +}; + +// Self destructing QIODevice for FormData +// For QNetworkAccessManager::put we will have to gurantee that the +// QIODevice is valid as long finished() of the QNetworkReply has not +// been emitted. With the presence of QNetworkReplyHandler::release I do +// not want to gurantee this. +class FormDataIODevice : public QIODevice { + Q_OBJECT +public: + FormDataIODevice(FormData*, QObject* parent = 0); + ~FormDataIODevice(); + + bool isSequential() const; + qint64 getFormDataSize() const { return m_fileSize + m_dataSize; } + +protected: + qint64 readData(char*, qint64); + qint64 writeData(const char*, qint64); + +private: + void moveToNextElement(); + qint64 computeSize(); + void openFileForCurrentElement(); + +private: + Vector<FormDataElement> m_formElements; + QFile* m_currentFile; + qint64 m_currentDelta; + qint64 m_fileSize; + qint64 m_dataSize; +}; + +} + +#endif // QNetworkReplyHandler_h diff --git a/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp new file mode 100644 index 0000000..0caeb05 --- /dev/null +++ b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp @@ -0,0 +1,190 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + 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 "QtNAMThreadSafeProxy.h" + +#include "Assertions.h" +#include <QAbstractNetworkCache> +#include <QNetworkAccessManager> +#include <QNetworkCookieJar> +#include <QStringList> + +// Use unused variables to be able to call qRegisterMetaType statically. +static int dummyStaticVar1 = qRegisterMetaType<QFutureInterface<bool> >("QFutureInterface<bool>"); +static int dummyStaticVar2 = qRegisterMetaType<QFutureInterface<QList<QNetworkCookie> > >("QFutureInterface<QList<QNetworkCookie> >"); + +namespace WebCore { + +QtNAMThreadSafeProxy::QtNAMThreadSafeProxy(QNetworkAccessManager *manager) + : m_manager(manager) +{ + moveToThread(manager->thread()); + + connect(this, SIGNAL(localSetCookiesRequested(const QUrl&, const QString&)), SLOT(localSetCookies(const QUrl&, const QString&))); + connect(this, SIGNAL(localCookiesForUrlRequested(QFutureInterface<QList<QNetworkCookie> >, const QUrl&)), SLOT(localCookiesForUrl(QFutureInterface<QList<QNetworkCookie> >, const QUrl&))); + connect(this, SIGNAL(localWillLoadFromCacheRequested(QFutureInterface<bool>, const QUrl&)), SLOT(localWillLoadFromCache(QFutureInterface<bool>, const QUrl&))); +} + +void QtNAMThreadSafeProxy::localSetCookies(const QUrl& url, const QString& cookies) +{ + QList<QNetworkCookie> cookieList = QNetworkCookie::parseCookies(cookies.toAscii()); + QList<QNetworkCookie>::Iterator it = cookieList.begin(); + while (it != cookieList.end()) { + if (it->isHttpOnly()) + it = cookieList.erase(it); + else + ++it; + } + m_manager->cookieJar()->setCookiesFromUrl(cookieList, url); +} + +void QtNAMThreadSafeProxy::localCookiesForUrl(QFutureInterface<QList<QNetworkCookie> > fi, const QUrl& url) +{ + fi.reportResult(m_manager->cookieJar()->cookiesForUrl(url)); + fi.reportFinished(); +} + +void QtNAMThreadSafeProxy::localWillLoadFromCache(QFutureInterface<bool> fi, const QUrl& url) +{ + bool retVal = false; + if (m_manager->cache()) + retVal = m_manager->cache()->metaData(url).isValid(); + + fi.reportFinished(&retVal); +} + +QtNetworkReplyThreadSafeProxy::QtNetworkReplyThreadSafeProxy(QNetworkAccessManager *manager) + : m_manager(manager) + , m_reply(0) + , m_forwardingDefered(false) + , m_contentLengthHeader(0) + , m_error(QNetworkReply::NoError) + , m_httpStatusCode(0) +{ + moveToThread(manager->thread()); + + // This might be unnecessarily heavy to do for each request while we could have the same wrapper for the manager instead + connect(this, SIGNAL(localGetRequested(const QNetworkRequest&)), SLOT(localGet(const QNetworkRequest&))); + connect(this, SIGNAL(localPostRequested(const QNetworkRequest&, QIODevice*)), SLOT(localPost(const QNetworkRequest&, QIODevice*))); + connect(this, SIGNAL(localHeadRequested(const QNetworkRequest&)), SLOT(localHead(const QNetworkRequest&))); + connect(this, SIGNAL(localPutRequested(const QNetworkRequest&, QIODevice*)), SLOT(localPut(const QNetworkRequest&, QIODevice*))); + connect(this, SIGNAL(localDeleteResourceRequested(const QNetworkRequest&)), SLOT(localDeleteResource(const QNetworkRequest&))); + connect(this, SIGNAL(localCustomRequestRequested(const QNetworkRequest&, const QByteArray&)), SLOT(localCustomRequest(const QNetworkRequest&, const QByteArray&))); + connect(this, SIGNAL(localAbortRequested()), SLOT(localAbort())); + connect(this, SIGNAL(localSetForwardingDeferedRequested(bool)), SLOT(localSetForwardingDefered(bool))); +} + +QtNetworkReplyThreadSafeProxy::~QtNetworkReplyThreadSafeProxy() +{ + delete m_reply; +} + +void QtNetworkReplyThreadSafeProxy::localGet(const QNetworkRequest& request) +{ + localSetReply(m_manager->get(request)); +} + +void QtNetworkReplyThreadSafeProxy::localPost(const QNetworkRequest& request, QIODevice* data) +{ + localSetReply(m_manager->post(request, data)); +} + +void QtNetworkReplyThreadSafeProxy::localHead(const QNetworkRequest& request) +{ + localSetReply(m_manager->head(request)); +} + +void QtNetworkReplyThreadSafeProxy::localPut(const QNetworkRequest& request, QIODevice* data) +{ + localSetReply(m_manager->put(request, data)); +} + +void QtNetworkReplyThreadSafeProxy::localDeleteResource(const QNetworkRequest& request) +{ + localSetReply(m_manager->deleteResource(request)); +} + +void QtNetworkReplyThreadSafeProxy::localCustomRequest(const QNetworkRequest& request, const QByteArray& verb) +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + localSetReply(m_manager->sendCustomRequest(request, verb)); +#endif +} + +void QtNetworkReplyThreadSafeProxy::localAbort() +{ + if (m_reply) + m_reply->abort(); +} + +void QtNetworkReplyThreadSafeProxy::localForwardData() +{ + if (m_reply->bytesAvailable()) { + QByteArray data = m_reply->read(m_reply->bytesAvailable()); + emit dataReceived(data); + } +} + +void QtNetworkReplyThreadSafeProxy::localSetForwardingDefered(bool forwardingDefered) +{ + if (m_forwardingDefered && !forwardingDefered) + localForwardData(); + m_forwardingDefered = forwardingDefered; +} + +void QtNetworkReplyThreadSafeProxy::localMirrorMembers() +{ + ASSERT(m_reply); + QMutexLocker lock(&m_mirroredMembersMutex); + + m_contentDispositionHeader = m_reply->rawHeader("Content-Disposition"); + m_contentLengthHeader = m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(); + m_contentTypeHeader = m_reply->header(QNetworkRequest::ContentTypeHeader).toString(); + m_error = m_reply->error(); + m_errorString = m_reply->errorString(); + m_httpReasonPhrase = m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray(); + m_httpStatusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + m_url = m_reply->url(); + m_redirectionTarget = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + m_rawHeaderPairs = m_reply->rawHeaderPairs(); +#else + m_rawHeaderPairs.clear(); + foreach (const QByteArray& headerName, m_reply->rawHeaderList()) + m_rawHeaderPairs.append(RawHeaderPair(headerName, m_reply->rawHeader(headerName))); +#endif +} + +void QtNetworkReplyThreadSafeProxy::localSetReply(QNetworkReply *reply) +{ + ASSERT(!m_reply); + m_reply = reply; + m_reply->setParent(0); + connect(m_reply, SIGNAL(readyRead()), this, SLOT(localForwardData())); + // Make sure localMirrorMembers() is called before the outward signal + connect(m_reply, SIGNAL(finished()), this, SLOT(localMirrorMembers()), Qt::DirectConnection); + connect(m_reply, SIGNAL(finished()), this, SIGNAL(finished())); + // Make sure localMirrorMembers() is called before the outward signal + connect(m_reply, SIGNAL(metaDataChanged()), this, SLOT(localMirrorMembers()), Qt::DirectConnection); + connect(m_reply, SIGNAL(metaDataChanged()), this, SIGNAL(metaDataChanged())); + connect(m_reply, SIGNAL(uploadProgress(qint64, qint64)), this, SIGNAL(uploadProgress(qint64, qint64))); +} + + +} diff --git a/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h new file mode 100644 index 0000000..a2fa4ee --- /dev/null +++ b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h @@ -0,0 +1,188 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + 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 QtNAMThreadSafeProxy_h +#define QtNAMThreadSafeProxy_h + +#include <QFuture> +#include <QMutex> +#include <QNetworkCookie> +#include <QNetworkReply> +#include <QObject> + +QT_BEGIN_NAMESPACE +class QNetworkAccessManager; +class QNetworkRequest; +class QUrl; +QT_END_NAMESPACE + +namespace WebCore { + +class QtNAMThreadSafeProxy : public QObject { + Q_OBJECT +public: + QtNAMThreadSafeProxy(QNetworkAccessManager *manager); + + void setCookies(const QUrl& url, const QString& cookies) + { + emit localSetCookiesRequested(url, cookies); + } + + QFuture<QList<QNetworkCookie> > cookiesForUrl(const QUrl& url) + { + QFutureInterface<QList<QNetworkCookie> > fi; + fi.reportStarted(); + emit localCookiesForUrlRequested(fi, url); + return fi.future(); + } + + QFuture<bool> willLoadFromCache(const QUrl& url) + { + QFutureInterface<bool> fi; + fi.reportStarted(); + emit localWillLoadFromCacheRequested(fi, url); + return fi.future(); + } + +signals: + void localSetCookiesRequested(const QUrl& url, const QString& cookies); + void localCookiesForUrlRequested(QFutureInterface<QList<QNetworkCookie> > fi, const QUrl& url); + void localWillLoadFromCacheRequested(QFutureInterface<bool> fi, const QUrl& url); + +private slots: + void localSetCookies(const QUrl& url, const QString& cookies); + void localCookiesForUrl(QFutureInterface<QList<QNetworkCookie> > fi, const QUrl& url); + void localWillLoadFromCache(QFutureInterface<bool> fi, const QUrl& url); + +private: + QNetworkAccessManager* m_manager; +}; + + +class QtNetworkReplyThreadSafeProxy : public QObject { + Q_OBJECT +public: + typedef QPair<QByteArray, QByteArray> RawHeaderPair; + QtNetworkReplyThreadSafeProxy(QNetworkAccessManager *manager); + ~QtNetworkReplyThreadSafeProxy(); + void abort() + { + emit localAbortRequested(); + } + void setForwardingDefered(bool forwardingDefered) + { + emit localSetForwardingDeferedRequested(forwardingDefered); + } + + QByteArray contentDispositionHeader() { QMutexLocker lock(&m_mirroredMembersMutex); return m_contentDispositionHeader; } + qlonglong contentLengthHeader() { QMutexLocker lock(&m_mirroredMembersMutex); return m_contentLengthHeader; } + QString contentTypeHeader() { QMutexLocker lock(&m_mirroredMembersMutex); return m_contentTypeHeader; } + QNetworkReply::NetworkError error() { QMutexLocker lock(&m_mirroredMembersMutex); return m_error; } + QString errorString() { QMutexLocker lock(&m_mirroredMembersMutex); return m_errorString; } + QByteArray httpReasonPhrase() { QMutexLocker lock(&m_mirroredMembersMutex); return m_httpReasonPhrase; } + int httpStatusCode() { QMutexLocker lock(&m_mirroredMembersMutex); return m_httpStatusCode; } + QUrl url() { QMutexLocker lock(&m_mirroredMembersMutex); return m_url; } + QUrl redirectionTarget() { QMutexLocker lock(&m_mirroredMembersMutex); return m_redirectionTarget; } + QList<RawHeaderPair> rawHeaderPairs() { QMutexLocker lock(&m_mirroredMembersMutex); return m_rawHeaderPairs; } + + QNetworkReply* reply() + { + // Careful, acccessing the reply accross threads might be hazardous to your health + return m_reply; + } +public: + void get(const QNetworkRequest &request) + { + emit localGetRequested(request); + } + void post(const QNetworkRequest &request, QIODevice* data) + { + emit localPostRequested(request, data); + } + void head(const QNetworkRequest &request) + { + emit localHeadRequested(request); + } + void put(const QNetworkRequest &request, QIODevice* data) + { + emit localPutRequested(request, data); + } + void deleteResource(const QNetworkRequest &request) + { + emit localDeleteResourceRequested(request); + } +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + void sendCustomRequest(const QNetworkRequest &request, const QByteArray& verb) + { + emit localCustomRequestRequested(request, verb); + } +#endif + +signals: + void localGetRequested(const QNetworkRequest& request); + void localPostRequested(const QNetworkRequest& request, QIODevice* data); + void localHeadRequested(const QNetworkRequest& request); + void localPutRequested(const QNetworkRequest& request, QIODevice* data); + void localDeleteResourceRequested(const QNetworkRequest& request); + void localCustomRequestRequested(const QNetworkRequest& request, const QByteArray& verb); + void localAbortRequested(); + void localSetForwardingDeferedRequested(bool forwardingDefered); + + void finished(); + void readyRead(); + void metaDataChanged(); + void uploadProgress(qint64 bytesSent, qint64 bytesTotal); + void dataReceived(const QByteArray &data); + +private slots: + void localGet(const QNetworkRequest& request); + void localPost(const QNetworkRequest& request, QIODevice* data); + void localHead(const QNetworkRequest& request); + void localPut(const QNetworkRequest& request, QIODevice* data); + void localDeleteResource(const QNetworkRequest& request); + void localCustomRequest(const QNetworkRequest& request, const QByteArray& verb); + void localAbort(); + void localForwardData(); + void localSetForwardingDefered(bool forwardingDefered); + void localMirrorMembers(); + +private: + void localSetReply(QNetworkReply *reply); + + QNetworkAccessManager *m_manager; + QNetworkReply *m_reply; + bool m_forwardingDefered; + + // Mirrored members + QMutex m_mirroredMembersMutex; + QByteArray m_contentDispositionHeader; + qlonglong m_contentLengthHeader; + QString m_contentTypeHeader; + QNetworkReply::NetworkError m_error; + QString m_errorString; + QByteArray m_httpReasonPhrase; + int m_httpStatusCode; + QUrl m_url; + QUrl m_redirectionTarget; + QList<RawHeaderPair> m_rawHeaderPairs; +}; + +} + + +#endif // QtNAMThreadSafeProxy_h diff --git a/Source/WebCore/platform/network/qt/ResourceError.h b/Source/WebCore/platform/network/qt/ResourceError.h new file mode 100644 index 0000000..ca8d36b --- /dev/null +++ b/Source/WebCore/platform/network/qt/ResourceError.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceError_h +#define ResourceError_h + +#include "ResourceErrorBase.h" + +namespace WebCore { + +class ResourceError : public ResourceErrorBase +{ +public: + ResourceError() + { + } + + ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) + : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) + { + } +}; + +} + +#endif // ResourceError_h_ diff --git a/Source/WebCore/platform/network/qt/ResourceHandleQt.cpp b/Source/WebCore/platform/network/qt/ResourceHandleQt.cpp new file mode 100644 index 0000000..56125b7 --- /dev/null +++ b/Source/WebCore/platform/network/qt/ResourceHandleQt.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * 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 COMPUTER, 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 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 "ResourceHandle.h" + +#include "ChromeClientQt.h" +#include "CachedResourceLoader.h" +#include "Frame.h" +#include "FrameNetworkingContext.h" +#include "FrameLoaderClientQt.h" +#include "QtNAMThreadSafeProxy.h" +#include "NotImplemented.h" +#include "Page.h" +#include "QNetworkReplyHandler.h" +#include "ResourceHandleClient.h" +#include "ResourceHandleInternal.h" +#include "SharedBuffer.h" + +// FIXME: WebCore including these headers from WebKit is a massive layering violation. +#include "qwebframe_p.h" + +#include <QAbstractNetworkCache> +#include <QCoreApplication> +#include <QUrl> +#include <QNetworkAccessManager> +#include <QNetworkRequest> +#include <QNetworkReply> + +namespace WebCore { + +class WebCoreSynchronousLoader : public ResourceHandleClient { +public: + WebCoreSynchronousLoader(); + + void waitForCompletion(); + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); + virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/); + virtual void didFail(ResourceHandle*, const ResourceError&); + + ResourceResponse resourceResponse() const { return m_response; } + ResourceError resourceError() const { return m_error; } + Vector<char> data() const { return m_data; } + +private: + ResourceResponse m_response; + ResourceError m_error; + Vector<char> m_data; + QEventLoop m_eventLoop; +}; + +WebCoreSynchronousLoader::WebCoreSynchronousLoader() +{ +} + +void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) +{ + m_response = response; +} + +void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int) +{ + m_data.append(data, length); +} + +void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*, double) +{ + m_eventLoop.exit(); +} + +void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error) +{ + m_error = error; + m_eventLoop.exit(); +} + +void WebCoreSynchronousLoader::waitForCompletion() +{ + m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents); +} + +ResourceHandleInternal::~ResourceHandleInternal() +{ +} + +ResourceHandle::~ResourceHandle() +{ + if (d->m_job) + cancel(); +} + +bool ResourceHandle::start(NetworkingContext* context) +{ + // If NetworkingContext is invalid then we are no longer attached to a Page, + // this must be an attempted load from an unload event handler, so let's just block it. + if (context && !context->isValid()) + return false; + + if (!d->m_user.isEmpty() || !d->m_pass.isEmpty()) { + // If credentials were specified for this request, add them to the url, + // so that they will be passed to QNetworkRequest. + KURL urlWithCredentials(firstRequest().url()); + urlWithCredentials.setUser(d->m_user); + urlWithCredentials.setPass(d->m_pass); + d->m_firstRequest.setURL(urlWithCredentials); + } + + getInternal()->m_context = context; + ResourceHandleInternal *d = getInternal(); + d->m_job = new QNetworkReplyHandler(this, QNetworkReplyHandler::LoadMode(d->m_defersLoading)); + return true; +} + +void ResourceHandle::cancel() +{ + if (d->m_job) { + d->m_job->abort(); + d->m_job = 0; + } +} + +bool ResourceHandle::loadsBlocked() +{ + return false; +} + +bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame* frame) +{ + if (!frame) + return false; + + if (frame->loader()->networkingContext()) { + QNetworkAccessManager* manager = frame->loader()->networkingContext()->networkAccessManager(); + QtNAMThreadSafeProxy managerProxy(manager); + if (managerProxy.willLoadFromCache(request.url())) { + request.setCachePolicy(ReturnCacheDataDontLoad); + return true; + } + } + + return false; +} + +bool ResourceHandle::supportsBufferedData() +{ + return false; +} + +PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() +{ + ASSERT_NOT_REACHED(); + return 0; +} + +void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials /*storedCredentials*/, ResourceError& error, ResourceResponse& response, Vector<char>& data) +{ + WebCoreSynchronousLoader syncLoader; + RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, &syncLoader, true, false)); + + ResourceHandleInternal* d = handle->getInternal(); + if (!d->m_user.isEmpty() || !d->m_pass.isEmpty()) { + // If credentials were specified for this request, add them to the url, + // so that they will be passed to QNetworkRequest. + KURL urlWithCredentials(d->m_firstRequest.url()); + urlWithCredentials.setUser(d->m_user); + urlWithCredentials.setPass(d->m_pass); + d->m_firstRequest.setURL(urlWithCredentials); + } + d->m_context = context; + d->m_job = new QNetworkReplyHandler(handle.get(), QNetworkReplyHandler::LoadNormal); + + syncLoader.waitForCompletion(); + error = syncLoader.resourceError(); + data = syncLoader.data(); + response = syncLoader.resourceResponse(); +} + +void ResourceHandle::platformSetDefersLoading(bool defers) +{ + if (d->m_job) + d->m_job->setLoadMode(QNetworkReplyHandler::LoadMode(defers)); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/qt/ResourceRequest.h b/Source/WebCore/platform/network/qt/ResourceRequest.h new file mode 100644 index 0000000..0c7b62b --- /dev/null +++ b/Source/WebCore/platform/network/qt/ResourceRequest.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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 COMPUTER, 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 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 ResourceRequest_h +#define ResourceRequest_h + +#include "ResourceRequestBase.h" + +QT_BEGIN_NAMESPACE +class QNetworkRequest; +class QObject; +QT_END_NAMESPACE + +namespace WebCore { + + class ResourceRequest : public ResourceRequestBase { + public: + ResourceRequest(const String& url) + : ResourceRequestBase(KURL(ParsedURLString, url), UseProtocolCachePolicy) + { + } + + ResourceRequest(const KURL& url) + : ResourceRequestBase(url, UseProtocolCachePolicy) + { + } + + ResourceRequest(const KURL& url, const String& referrer, ResourceRequestCachePolicy policy = UseProtocolCachePolicy) + : ResourceRequestBase(url, policy) + { + setHTTPReferrer(referrer); + } + + ResourceRequest() + : ResourceRequestBase(KURL(), UseProtocolCachePolicy) + { + } + + QNetworkRequest toNetworkRequest(QObject* originatingObject) const; + + private: + friend class ResourceRequestBase; + + void doUpdatePlatformRequest() {} + void doUpdateResourceRequest() {} + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } + }; + + struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { + }; + +} // namespace WebCore + +#endif // ResourceRequest_h diff --git a/Source/WebCore/platform/network/qt/ResourceRequestQt.cpp b/Source/WebCore/platform/network/qt/ResourceRequestQt.cpp new file mode 100644 index 0000000..7e162ed --- /dev/null +++ b/Source/WebCore/platform/network/qt/ResourceRequestQt.cpp @@ -0,0 +1,98 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + 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 "ResourceRequest.h" + +#include <qglobal.h> + +#include <QNetworkRequest> +#include <QUrl> + +namespace WebCore { + +// Currently Qt allows three connections per host on symbian and six +// for everyone else. The limit can be found in qhttpnetworkconnection.cpp. +// To achieve the best result we want WebKit to schedule the jobs so we +// are using the limit as found in Qt. To allow Qt to fill its queue +// and prepare jobs we will schedule two more downloads. +// Per TCP connection there is 1 current processed, 3 possibly pipelined +// and 2 ready to re-fill the pipeline. +unsigned initializeMaximumHTTPConnectionCountPerHost() +{ +#ifdef Q_OS_SYMBIAN + return 3 * (1 + 3 + 2); +#else + return 6 * (1 + 3 + 2); +#endif +} + +QNetworkRequest ResourceRequest::toNetworkRequest(QObject* originatingFrame) const +{ + QNetworkRequest request; + request.setUrl(url()); + request.setOriginatingObject(originatingFrame); + + const HTTPHeaderMap &headers = httpHeaderFields(); + for (HTTPHeaderMap::const_iterator it = headers.begin(), end = headers.end(); + it != end; ++it) { + QByteArray name = QString(it->first).toAscii(); + QByteArray value = QString(it->second).toAscii(); + // QNetworkRequest::setRawHeader() would remove the header if the value is null + // Make sure to set an empty header instead of null header. + if (!value.isNull()) + request.setRawHeader(name, value); + else + request.setRawHeader(name, ""); + } + + // Make sure we always have an Accept header; some sites require this to + // serve subresources + if (!request.hasRawHeader("Accept")) + request.setRawHeader("Accept", "*/*"); + + switch (cachePolicy()) { + case ReloadIgnoringCacheData: + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + break; + case ReturnCacheDataElseLoad: + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + break; + case ReturnCacheDataDontLoad: + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache); + break; + case UseProtocolCachePolicy: + // QNetworkRequest::PreferNetwork + default: + break; + } + +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + if (!allowCookies()) { + request.setAttribute(QNetworkRequest::CookieLoadControlAttribute, QNetworkRequest::Manual); + request.setAttribute(QNetworkRequest::CookieSaveControlAttribute, QNetworkRequest::Manual); + request.setAttribute(QNetworkRequest::AuthenticationReuseAttribute, QNetworkRequest::Manual); + } +#endif + + return request; +} + +} + diff --git a/Source/WebCore/platform/network/qt/ResourceResponse.h b/Source/WebCore/platform/network/qt/ResourceResponse.h new file mode 100644 index 0000000..288cf84 --- /dev/null +++ b/Source/WebCore/platform/network/qt/ResourceResponse.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceResponse_h +#define ResourceResponse_h + +#include "ResourceResponseBase.h" + +namespace WebCore { + +class ResourceResponse : public ResourceResponseBase { +public: + ResourceResponse() + { + } + + ResourceResponse(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) + : 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 + +#endif // ResourceResponse_h diff --git a/Source/WebCore/platform/network/qt/SocketStreamError.h b/Source/WebCore/platform/network/qt/SocketStreamError.h new file mode 100644 index 0000000..f9641ad --- /dev/null +++ b/Source/WebCore/platform/network/qt/SocketStreamError.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamError_h +#define SocketStreamError_h + +#include "SocketStreamErrorBase.h" + +namespace WebCore { + + class SocketStreamError : public SocketStreamErrorBase { + public: + SocketStreamError() { } + explicit SocketStreamError(int errorCode) + : SocketStreamErrorBase(errorCode) + { + } + + }; + +} // namespace WebCore + +#endif // SocketStreamError_h diff --git a/Source/WebCore/platform/network/qt/SocketStreamHandle.h b/Source/WebCore/platform/network/qt/SocketStreamHandle.h new file mode 100644 index 0000000..e725344 --- /dev/null +++ b/Source/WebCore/platform/network/qt/SocketStreamHandle.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010 Nokia Inc. All rights reserved. + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamHandle_h +#define SocketStreamHandle_h + +#include "SocketStreamHandleBase.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +#if !PLATFORM(QT) +#error This should only be built on Qt +#endif + +namespace WebCore { + + class AuthenticationChallenge; + class Credential; + class SocketStreamHandleClient; + class SocketStreamHandlePrivate; + + class SocketStreamHandle : public RefCounted<SocketStreamHandle>, public SocketStreamHandleBase { + public: + static PassRefPtr<SocketStreamHandle> create(const KURL& url, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(url, client)); } + + virtual ~SocketStreamHandle(); + + protected: + virtual int platformSend(const char* data, int length); + virtual void platformClose(); + + private: + SocketStreamHandle(const KURL&, SocketStreamHandleClient*); + + // No authentication for streams per se, but proxy may ask for credentials. + void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); + void receivedCredential(const AuthenticationChallenge&, const Credential&); + void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&); + void receivedCancellation(const AuthenticationChallenge&); + SocketStreamHandlePrivate* m_p; + friend class SocketStreamHandlePrivate; + }; + +} // namespace WebCore + +#endif // SocketStreamHandle_h diff --git a/Source/WebCore/platform/network/qt/SocketStreamHandlePrivate.h b/Source/WebCore/platform/network/qt/SocketStreamHandlePrivate.h new file mode 100644 index 0000000..d074f42 --- /dev/null +++ b/Source/WebCore/platform/network/qt/SocketStreamHandlePrivate.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Nokia 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamHandlePrivate_h +#define SocketStreamHandlePrivate_h + +#include "SocketStreamHandleBase.h" + +#include <QSslSocket> +#include <QTcpSocket> + +namespace WebCore { + +class AuthenticationChallenge; +class Credential; +class SocketStreamHandleClient; +class SocketStreamHandlePrivate; + +class SocketStreamHandlePrivate : public QObject { + Q_OBJECT +public: + SocketStreamHandlePrivate(SocketStreamHandle*, const KURL&); + ~SocketStreamHandlePrivate(); + +public slots: + void socketConnected(); + void socketReadyRead(); + int send(const char* data, int len); + void close(); + void socketSentData(); + void socketClosed(); + void socketError(QAbstractSocket::SocketError); + void socketClosedCallback(); + void socketErrorCallback(int); +#ifndef QT_NO_OPENSSL + void socketSslErrors(const QList<QSslError>&); +#endif +public: + QTcpSocket* m_socket; + SocketStreamHandle* m_streamHandle; +}; + +} + +#endif diff --git a/Source/WebCore/platform/network/qt/SocketStreamHandleQt.cpp b/Source/WebCore/platform/network/qt/SocketStreamHandleQt.cpp new file mode 100644 index 0000000..10550ae --- /dev/null +++ b/Source/WebCore/platform/network/qt/SocketStreamHandleQt.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2010 Nokia Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "SocketStreamHandle.h" + +#include "KURL.h" +#include "Logging.h" +#include "NotImplemented.h" +#include "SocketStreamHandleClient.h" +#include "SocketStreamHandlePrivate.h" + +namespace WebCore { + +SocketStreamHandlePrivate::SocketStreamHandlePrivate(SocketStreamHandle* streamHandle, const KURL& url) : QObject() +{ + m_streamHandle = streamHandle; + m_socket = 0; + bool isSecure = url.protocolIs("wss"); + + if (isSecure) { +#ifndef QT_NO_OPENSSL + m_socket = new QSslSocket(this); +#endif + } else + m_socket = new QTcpSocket(this); + + if (!m_socket) + return; + + connect(m_socket, SIGNAL(connected()), this, SLOT(socketConnected())); + connect(m_socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead())); + connect(m_socket, SIGNAL(disconnected()), this, SLOT(socketClosed())); + connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); + if (isSecure) + connect(m_socket, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(socketSslErrors(const QList<QSslError>&))); + + unsigned int port = url.hasPort() ? url.port() : (isSecure ? 443 : 80); + + QString host = url.host(); + if (isSecure) { +#ifndef QT_NO_OPENSSL + static_cast<QSslSocket*>(m_socket)->connectToHostEncrypted(host, port); +#endif + } else + m_socket->connectToHost(host, port); +} + +SocketStreamHandlePrivate::~SocketStreamHandlePrivate() +{ + Q_ASSERT(!(m_socket && m_socket->state() == QAbstractSocket::ConnectedState)); +} + +void SocketStreamHandlePrivate::socketConnected() +{ + if (m_streamHandle && m_streamHandle->client()) { + m_streamHandle->m_state = SocketStreamHandleBase::Open; + m_streamHandle->client()->didOpen(m_streamHandle); + } +} + +void SocketStreamHandlePrivate::socketReadyRead() +{ + if (m_streamHandle && m_streamHandle->client()) { + QByteArray data = m_socket->read(m_socket->bytesAvailable()); + m_streamHandle->client()->didReceiveData(m_streamHandle, data.constData(), data.size()); + } +} + +int SocketStreamHandlePrivate::send(const char* data, int len) +{ + if (!m_socket || m_socket->state() != QAbstractSocket::ConnectedState) + return 0; + quint64 sentSize = m_socket->write(data, len); + QMetaObject::invokeMethod(this, "socketSentData", Qt::QueuedConnection); + return sentSize; +} + +void SocketStreamHandlePrivate::close() +{ + if (m_socket && m_socket->state() == QAbstractSocket::ConnectedState) + m_socket->close(); +} + +void SocketStreamHandlePrivate::socketSentData() +{ + if (m_streamHandle) + m_streamHandle->sendPendingData(); +} + +void SocketStreamHandlePrivate::socketClosed() +{ + QMetaObject::invokeMethod(this, "socketClosedCallback", Qt::QueuedConnection); +} + +void SocketStreamHandlePrivate::socketError(QAbstractSocket::SocketError error) +{ + QMetaObject::invokeMethod(this, "socketErrorCallback", Qt::QueuedConnection, Q_ARG(int, error)); +} + +void SocketStreamHandlePrivate::socketClosedCallback() +{ + if (m_streamHandle && m_streamHandle->client()) { + SocketStreamHandle* streamHandle = m_streamHandle; + m_streamHandle = 0; + // This following call deletes _this_. Nothing should be after it. + streamHandle->client()->didClose(streamHandle); + } +} + +void SocketStreamHandlePrivate::socketErrorCallback(int error) +{ + // FIXME - in the future, we might not want to treat all errors as fatal. + if (m_streamHandle && m_streamHandle->client()) { + SocketStreamHandle* streamHandle = m_streamHandle; + m_streamHandle = 0; + // This following call deletes _this_. Nothing should be after it. + streamHandle->client()->didClose(streamHandle); + } +} + +#ifndef QT_NO_OPENSSL +void SocketStreamHandlePrivate::socketSslErrors(const QList<QSslError>& error) +{ + QMetaObject::invokeMethod(this, "socketErrorCallback", Qt::QueuedConnection, Q_ARG(int, error[0].error())); +} +#endif + +SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient* client) + : SocketStreamHandleBase(url, client) +{ + LOG(Network, "SocketStreamHandle %p new client %p", this, m_client); + m_p = new SocketStreamHandlePrivate(this, url); +} + +SocketStreamHandle::~SocketStreamHandle() +{ + LOG(Network, "SocketStreamHandle %p delete", this); + setClient(0); + delete m_p; +} + +int SocketStreamHandle::platformSend(const char* data, int len) +{ + LOG(Network, "SocketStreamHandle %p platformSend", this); + return m_p->send(data, len); +} + +void SocketStreamHandle::platformClose() +{ + LOG(Network, "SocketStreamHandle %p platformClose", this); + m_p->close(); +} + +void SocketStreamHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&) +{ + notImplemented(); +} + +} // namespace WebCore + +#include "moc_SocketStreamHandlePrivate.cpp" diff --git a/Source/WebCore/platform/network/soup/AuthenticationChallenge.h b/Source/WebCore/platform/network/soup/AuthenticationChallenge.h new file mode 100644 index 0000000..5177f1e --- /dev/null +++ b/Source/WebCore/platform/network/soup/AuthenticationChallenge.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2007 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 COMPUTER, 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 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 AuthenticationChallenge_h +#define AuthenticationChallenge_h + +#include "AuthenticationChallengeBase.h" + +namespace WebCore { + +class AuthenticationChallenge : public AuthenticationChallengeBase { +public: + AuthenticationChallenge() + { + } + + AuthenticationChallenge(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error) + : AuthenticationChallengeBase(protectionSpace, proposedCredential, previousFailureCount, response, error) + { + } +}; + +} + +#endif diff --git a/Source/WebCore/platform/network/soup/CookieJarSoup.cpp b/Source/WebCore/platform/network/soup/CookieJarSoup.cpp new file mode 100644 index 0000000..ba29622 --- /dev/null +++ b/Source/WebCore/platform/network/soup/CookieJarSoup.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2008 Xan Lopez <xan@gnome.org> + * Copyright (C) 2009 Igalia S.L. + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "CookieJarSoup.h" + +#include "Cookie.h" +#include "Document.h" +#include "GOwnPtrSoup.h" +#include "KURL.h" +#include <wtf/text/CString.h> + +namespace WebCore { + +static bool cookiesInitialized; +static SoupCookieJar* cookieJar; + +SoupCookieJar* defaultCookieJar() +{ + if (!cookiesInitialized) { + cookiesInitialized = true; + + cookieJar = soup_cookie_jar_new(); +#ifdef HAVE_LIBSOUP_2_29_90 + soup_cookie_jar_set_accept_policy(cookieJar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); +#endif + } + + return cookieJar; +} + +void setDefaultCookieJar(SoupCookieJar* jar) +{ + cookiesInitialized = true; + + if (cookieJar) + g_object_unref(cookieJar); + + cookieJar = jar; + + if (cookieJar) + g_object_ref(cookieJar); +} + +void setCookies(Document* document, const KURL& url, const String& value) +{ + SoupCookieJar* jar = defaultCookieJar(); + if (!jar) + return; + + GOwnPtr<SoupURI> origin(soup_uri_new(url.string().utf8().data())); + +#ifdef HAVE_LIBSOUP_2_29_90 + GOwnPtr<SoupURI> firstParty(soup_uri_new(document->firstPartyForCookies().string().utf8().data())); + + soup_cookie_jar_set_cookie_with_first_party(jar, + origin.get(), + firstParty.get(), + value.utf8().data()); +#else + soup_cookie_jar_set_cookie(jar, + origin.get(), + value.utf8().data()); +#endif +} + +String cookies(const Document* /*document*/, const KURL& url) +{ + SoupCookieJar* jar = defaultCookieJar(); + if (!jar) + return String(); + + SoupURI* uri = soup_uri_new(url.string().utf8().data()); + char* cookies = soup_cookie_jar_get_cookies(jar, uri, FALSE); + soup_uri_free(uri); + + String result(String::fromUTF8(cookies)); + g_free(cookies); + + return result; +} + +String cookieRequestHeaderFieldValue(const Document* /*document*/, const KURL& url) +{ + SoupCookieJar* jar = defaultCookieJar(); + if (!jar) + return String(); + + SoupURI* uri = soup_uri_new(url.string().utf8().data()); + char* cookies = soup_cookie_jar_get_cookies(jar, uri, TRUE); + soup_uri_free(uri); + + String result(String::fromUTF8(cookies)); + g_free(cookies); + + return result; +} + +bool cookiesEnabled(const Document* /*document*/) +{ + return defaultCookieJar(); +} + +bool getRawCookies(const Document*, const KURL&, Vector<Cookie>& rawCookies) +{ + // FIXME: Not yet implemented + rawCookies.clear(); + return false; // return true when implemented +} + +void deleteCookie(const Document*, const KURL&, const String&) +{ + // FIXME: Not yet implemented +} + +} diff --git a/Source/WebCore/platform/network/soup/CookieJarSoup.h b/Source/WebCore/platform/network/soup/CookieJarSoup.h new file mode 100644 index 0000000..ab1f95c --- /dev/null +++ b/Source/WebCore/platform/network/soup/CookieJarSoup.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008 Xan Lopez <xan@gnome.org> + * Copyright (C) 2008 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 COMPUTER, 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 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 CookieJarSoup_h +#define CookieJarSoup_h + +#include "CookieJar.h" +#include <libsoup/soup.h> + +namespace WebCore { + SoupCookieJar* defaultCookieJar(); + void setDefaultCookieJar(SoupCookieJar* jar); +} + +#endif diff --git a/Source/WebCore/platform/network/soup/GOwnPtrSoup.cpp b/Source/WebCore/platform/network/soup/GOwnPtrSoup.cpp new file mode 100644 index 0000000..56fe692 --- /dev/null +++ b/Source/WebCore/platform/network/soup/GOwnPtrSoup.cpp @@ -0,0 +1,33 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "config.h" +#include "GOwnPtrSoup.h" + +#include <libsoup/soup-uri.h> + +namespace WTF { + +template <> void freeOwnedGPtr<SoupURI>(SoupURI* ptr) +{ + if (ptr) + soup_uri_free(ptr); +} + +} diff --git a/Source/WebCore/platform/network/soup/GOwnPtrSoup.h b/Source/WebCore/platform/network/soup/GOwnPtrSoup.h new file mode 100644 index 0000000..c129f84 --- /dev/null +++ b/Source/WebCore/platform/network/soup/GOwnPtrSoup.h @@ -0,0 +1,33 @@ +/* + * 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 GOwnPtrSoup_h +#define GOwnPtrSoup_h + +#include "GOwnPtr.h" + +typedef struct _SoupURI SoupURI; + +namespace WTF { + +template<> void freeOwnedGPtr<SoupURI>(SoupURI* ptr); + +} + +#endif diff --git a/Source/WebCore/platform/network/soup/ProxyServerSoup.cpp b/Source/WebCore/platform/network/soup/ProxyServerSoup.cpp new file mode 100644 index 0000000..50b32de --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/ResourceError.h b/Source/WebCore/platform/network/soup/ResourceError.h new file mode 100644 index 0000000..2d11367 --- /dev/null +++ b/Source/WebCore/platform/network/soup/ResourceError.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceError_h +#define ResourceError_h + +#include "ResourceErrorBase.h" + +namespace WebCore { + +class ResourceError : public ResourceErrorBase +{ +public: + ResourceError() + { + } + + ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) + : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) + { + } +}; + +} + +#endif // ResourceError_h_ diff --git a/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp new file mode 100644 index 0000000..8900b18 --- /dev/null +++ b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -0,0 +1,915 @@ +/* + * Copyright (C) 2008 Alp Toker <alp@atoker.com> + * Copyright (C) 2008 Xan Lopez <xan@gnome.org> + * Copyright (C) 2008, 2010 Collabora Ltd. + * Copyright (C) 2009 Holger Hans Peter Freyther + * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org> + * Copyright (C) 2009 Christian Dywan <christian@imendio.com> + * Copyright (C) 2009 Igalia S.L. + * Copyright (C) 2009 John Kjellberg <john.kjellberg@power.alstom.com> + * + * 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 "ResourceHandle.h" + +#include "Base64.h" +#include "CString.h" +#include "ChromeClient.h" +#include "CookieJarSoup.h" +#include "CachedResourceLoader.h" +#include "FileSystem.h" +#include "Frame.h" +#include "GOwnPtrSoup.h" +#include "HTTPParsers.h" +#include "Logging.h" +#include "MIMETypeRegistry.h" +#include "NotImplemented.h" +#include "Page.h" +#include "ResourceError.h" +#include "ResourceHandleClient.h" +#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> +#include <glib.h> +#include <libsoup/soup.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +namespace WebCore { + +#define READ_BUFFER_SIZE 8192 + +class WebCoreSynchronousLoader : public ResourceHandleClient, public Noncopyable { +public: + WebCoreSynchronousLoader(ResourceError&, ResourceResponse &, Vector<char>&); + ~WebCoreSynchronousLoader(); + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); + virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/); + virtual void didFail(ResourceHandle*, const ResourceError&); + + void run(); + +private: + ResourceError& m_error; + ResourceResponse& m_response; + Vector<char>& m_data; + bool m_finished; + GMainLoop* m_mainLoop; +}; + +WebCoreSynchronousLoader::WebCoreSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data) + : m_error(error) + , m_response(response) + , m_data(data) + , m_finished(false) +{ + m_mainLoop = g_main_loop_new(0, false); +} + +WebCoreSynchronousLoader::~WebCoreSynchronousLoader() +{ + g_main_loop_unref(m_mainLoop); +} + +void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) +{ + m_response = response; +} + +void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int) +{ + m_data.append(data, length); +} + +void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*, double) +{ + g_main_loop_quit(m_mainLoop); + m_finished = true; +} + +void WebCoreSynchronousLoader::didFail(ResourceHandle* handle, const ResourceError& error) +{ + m_error = error; + didFinishLoading(handle, 0); +} + +void WebCoreSynchronousLoader::run() +{ + if (!m_finished) + g_main_loop_run(m_mainLoop); +} + +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_soupRequest) + g_object_set_data(G_OBJECT(m_soupRequest.get()), "webkit-resource", 0); + + if (m_idleHandler) { + g_source_remove(m_idleHandler); + m_idleHandler = 0; + } +} + +ResourceHandle::~ResourceHandle() +{ + cleanupSoupRequestOperation(this, true); +} + +void ResourceHandle::prepareForURL(const KURL &url) +{ +#ifdef HAVE_LIBSOUP_2_29_90 + GOwnPtr<SoupURI> soupURI(soup_uri_new(url.prettyURL().utf8().data())); + if (!soupURI) + return; + soup_session_prepare_for_uri(ResourceHandle::defaultSession(), soupURI.get()); +#endif +} + +// All other kinds of redirections, except for the *304* status code +// (SOUP_STATUS_NOT_MODIFIED) which needs to be fed into WebCore, will be +// handled by soup directly. +static gboolean statusWillBeHandledBySoup(guint statusCode) +{ + if (SOUP_STATUS_IS_TRANSPORT_ERROR(statusCode) + || (SOUP_STATUS_IS_REDIRECTION(statusCode) && (statusCode != SOUP_STATUS_NOT_MODIFIED)) + || (statusCode == SOUP_STATUS_UNAUTHORIZED)) + return true; + + return false; +} + +static void fillResponseFromMessage(SoupMessage* msg, ResourceResponse* response) +{ + response->updateFromSoupMessage(msg); +} + +// Called each time the message is going to be sent again except the first time. +// It's used mostly to let webkit know about redirects. +static void restartedCallback(SoupMessage* msg, gpointer data) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(data); + if (!handle) + return; + ResourceHandleInternal* d = handle->getInternal(); + if (d->m_cancelled) + return; + + 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(); + ResourceResponse response; + request.setURL(newURL); + request.setHTTPMethod(msg->method); + fillResponseFromMessage(msg, &response); + + // Should not set Referer after a redirect from a secure resource to non-secure one. + if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https")) { + request.clearHTTPReferrer(); + soup_message_headers_remove(msg->request_headers, "Referer"); + } + + if (d->client()) + d->client()->willSendRequest(handle, request, response); + + if (d->m_cancelled) + return; + +#ifdef HAVE_LIBSOUP_2_29_90 + // Update the first party in case the base URL changed with the redirect + String firstPartyString = request.firstPartyForCookies().string(); + if (!firstPartyString.isEmpty()) { + GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data())); + soup_message_set_first_party(d->m_soupMessage.get(), firstParty.get()); + } +#endif +} + +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. + // 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; + } + + // For all the other responses, we handle each chunk ourselves, + // and we don't need msg->response_body to contain all of the data + // we got, when we finish downloading. + soup_message_body_set_accumulate(msg->response_body, FALSE); + + RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); + + // The content-sniffed callback will handle the response if WebCore + // require us to sniff. + if (!handle || statusWillBeHandledBySoup(msg->status_code) || handle->shouldContentSniff()) + return; + + ResourceHandleInternal* d = handle->getInternal(); + if (d->m_cancelled) + return; + ResourceHandleClient* client = handle->client(); + if (!client) + return; + + ASSERT(d->m_response.isNull()); + + fillResponseFromMessage(msg, &d->m_response); + client->didReceiveResponse(handle.get(), d->m_response); +} + +// This callback will not be called if the content sniffer is disabled in startHttp. +static void contentSniffedCallback(SoupMessage* msg, const char* sniffedType, GHashTable *params, gpointer data) +{ + if (sniffedType) { + const char* officialType = soup_message_headers_get_one(msg->response_headers, "Content-Type"); + + if (!officialType || strcmp(officialType, sniffedType)) + soup_message_headers_set_content_type(msg->response_headers, sniffedType, params); + } + + if (statusWillBeHandledBySoup(msg->status_code)) + return; + + RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); + if (!handle) + return; + ResourceHandleInternal* d = handle->getInternal(); + if (d->m_cancelled) + return; + ResourceHandleClient* client = handle->client(); + if (!client) + return; + + ASSERT(d->m_response.isNull()); + + fillResponseFromMessage(msg, &d->m_response); + client->didReceiveResponse(handle.get(), d->m_response); +} + +static void gotChunkCallback(SoupMessage* msg, SoupBuffer* chunk, gpointer data) +{ + if (statusWillBeHandledBySoup(msg->status_code)) + return; + + RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); + if (!handle) + return; + ResourceHandleInternal* d = handle->getInternal(); + if (d->m_cancelled) + return; + ResourceHandleClient* client = handle->client(); + if (!client) + return; + + ASSERT(!d->m_response.isNull()); + + client->didReceiveData(handle.get(), chunk->data, chunk->length, false); +} + +static gboolean parseDataUrl(gpointer callbackData) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(callbackData); + ResourceHandleClient* client = handle->client(); + ResourceHandleInternal* d = handle->getInternal(); + if (d->m_cancelled) + return false; + + d->m_idleHandler = 0; + + ASSERT(client); + if (!client) + return false; + + String url = handle->firstRequest().url().string(); + ASSERT(url.startsWith("data:", false)); + + int index = url.find(','); + if (index == -1) { + client->cannotShowURL(handle); + return false; + } + + String mediaType = url.substring(5, index - 5); + + bool isBase64 = mediaType.endsWith(";base64", false); + if (isBase64) + mediaType = mediaType.left(mediaType.length() - 7); + + if (mediaType.isEmpty()) + mediaType = "text/plain;charset=US-ASCII"; + + String mimeType = extractMIMETypeFromMediaType(mediaType); + String charset = extractCharsetFromMediaType(mediaType); + + ASSERT(d->m_response.isNull()); + + d->m_response.setURL(handle->firstRequest().url()); + d->m_response.setMimeType(mimeType); + + // For non base64 encoded data we have to convert to UTF-16 early + // due to limitations in KURL + d->m_response.setTextEncodingName(isBase64 ? charset : "UTF-16"); + client->didReceiveResponse(handle, d->m_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; + + SoupSession* session = handle->defaultSession(); + GOwnPtr<GError> error; + d->m_soupRequest = adoptGRef(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 = adoptGRef(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 = adoptGRef(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; +} + +static bool startData(ResourceHandle* handle, String urlString) +{ + ASSERT(handle); + + ResourceHandleInternal* d = handle->getInternal(); + + // If parseDataUrl is called synchronously the job is not yet effectively started + // and webkit won't never know that the data has been parsed even didFinishLoading is called. + d->m_idleHandler = g_timeout_add(0, parseDataUrl, handle); + return true; +} + +static SoupSession* createSoupSession() +{ + return soup_session_async_new(); +} + +// Values taken from http://stevesouders.com/ua/index.php following +// the rule "Do What Every Other Modern Browser Is Doing". They seem +// to significantly improve page loading time compared to soup's +// default values. +#define MAX_CONNECTIONS 60 +#define MAX_CONNECTIONS_PER_HOST 6 + +static void ensureSessionIsInitialized(SoupSession* session) +{ + if (g_object_get_data(G_OBJECT(session), "webkit-init")) + return; + + SoupCookieJar* jar = reinterpret_cast<SoupCookieJar*>(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR)); + if (!jar) + soup_session_add_feature(session, SOUP_SESSION_FEATURE(defaultCookieJar())); + else + setDefaultCookieJar(jar); + + if (!soup_session_get_feature(session, SOUP_TYPE_LOGGER) && LogNetwork.state == WTFLogChannelOn) { + SoupLogger* logger = soup_logger_new(static_cast<SoupLoggerLogLevel>(SOUP_LOGGER_LOG_BODY), -1); + soup_logger_attach(logger, session); + g_object_unref(logger); + } + + g_object_set(session, + SOUP_SESSION_MAX_CONNS, MAX_CONNECTIONS, + SOUP_SESSION_MAX_CONNS_PER_HOST, MAX_CONNECTIONS_PER_HOST, + NULL); + + 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 ? static_cast<gint>(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)) { + ASSERT(d->m_response.isNull()); + + fillResponseFromMessage(soupMsg, &d->m_response); + client->didReceiveResponse(handle.get(), d->m_response); + + // WebCore might have cancelled the job in the while. We + // must check for response_body->length and not + // response_body->data as libsoup always creates the + // SoupBuffer for the body even if the length is 0 + if (!d->m_cancelled && soupMsg->response_body->length) + 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 = adoptGRef(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()); + + // Ensure a response is sent for any protocols that don't explicitly support responses + // through got-headers signal or content sniffing. + // (e.g. file and GIO based protocol). + if (!handle->shouldContentSniff() && d->m_response.isNull()) { + d->m_response.setURL(handle->firstRequest().url()); + d->m_response.setMimeType(webkit_soup_request_get_content_type(d->m_soupRequest.get())); + d->m_response.setExpectedContentLength(webkit_soup_request_get_content_length(d->m_soupRequest.get())); + client->didReceiveResponse(handle.get(), d->m_response); + + if (d->m_cancelled) { + cleanupSoupRequestOperation(handle.get()); + return; + } + } + + if (d->m_defersLoading) + soup_session_pause_message(handle->defaultSession(), d->m_soupMessage.get()); + + 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); + + SoupSession* session = handle->defaultSession(); + ensureSessionIsInitialized(session); + + ResourceHandleInternal* d = handle->getInternal(); + + ResourceRequest request(handle->firstRequest()); + KURL url(request.url()); + url.removeFragmentIdentifier(); + request.setURL(url); + + GOwnPtr<GError> error; + d->m_soupRequest = adoptGRef(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 = adoptGRef(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(soupMessage, SOUP_TYPE_CONTENT_SNIFFER); + else + g_signal_connect(soupMessage, "content-sniffed", G_CALLBACK(contentSniffedCallback), handle); + + g_signal_connect(soupMessage, "restarted", G_CALLBACK(restartedCallback), handle); + g_signal_connect(soupMessage, "got-headers", G_CALLBACK(gotHeadersCallback), 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(soupMessage, firstParty.get()); + } +#endif + + FormData* httpBody = d->m_firstRequest.httpBody(); + if (httpBody && !httpBody->isEmpty()) { + size_t numElements = httpBody->elements().size(); + + // handle the most common case (i.e. no file upload) + if (numElements < 2) { + Vector<char> body; + httpBody->flatten(body); + soup_message_set_request(soupMessage, d->m_firstRequest.httpContentType().utf8().data(), + SOUP_MEMORY_COPY, body.data(), body.size()); + } else { + /* + * we have more than one element to upload, and some may + * be (big) files, which we will want to mmap instead of + * copying into memory; TODO: support upload of non-local + * (think sftp://) files by using GIO? + */ + 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(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 + */ + GOwnPtr<GError> error; + CString fileName = fileSystemRepresentation(element.m_filename); + GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr()); + + if (error) { + g_signal_handlers_disconnect_matched(soupMessage, G_SIGNAL_MATCH_DATA, + 0, 0, 0, 0, handle); + d->m_soupMessage.clear(); + + return false; + } + + SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping), + g_mapped_file_get_length(fileMapping), + fileMapping, + reinterpret_cast<GDestroyNotify>(g_mapped_file_unref)); + soup_message_body_append_buffer(soupMessage->request_body, soupBuffer); + soup_buffer_free(soupBuffer); + } + } + } + } + + // 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(soupMessage->request_headers, "Accept")) + soup_message_headers_append(soupMessage->request_headers, "Accept", "*/*"); + + // Send the request only if it's not been explicitely deferred. + if (!d->m_defersLoading) { + d->m_cancellable = adoptGRef(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_soupMessage); + + // The frame could be null if the ResourceHandle is not associated to any + // Frame, e.g. if we are downloading a file. + // If the frame is not null but the page is null this must be an attempted + // load from an unload handler, so let's just block it. + // If both the frame and the page are not null the context is valid. + if (context && !context->isValid()) + return false; + + if (!(d->m_user.isEmpty() || d->m_pass.isEmpty())) { + // If credentials were specified for this request, add them to the url, + // so that they will be passed to NetworkRequest. + KURL urlWithCredentials(firstRequest().url()); + urlWithCredentials.setUser(d->m_user); + urlWithCredentials.setPass(d->m_pass); + d->m_firstRequest.setURL(urlWithCredentials); + } + + KURL url = firstRequest().url(); + String urlString = url.string(); + String protocol = url.protocol(); + + // Used to set the authentication dialog toplevel; may be NULL + d->m_context = context; + + if (equalIgnoringCase(protocol, "data")) + return startData(this, urlString); + + if (equalIgnoringCase(protocol, "http") || equalIgnoringCase(protocol, "https")) { + if (startHttp(this)) + return true; + } + + if (equalIgnoringCase(protocol, "file") || equalIgnoringCase(protocol, "ftp") || equalIgnoringCase(protocol, "ftps")) { + // FIXME: should we be doing any other protocols here? + if (startGio(this, url)) + return true; + } + + // Error must not be reported immediately + this->scheduleFailure(InvalidURLFailure); + + return true; +} + +void ResourceHandle::cancel() +{ + d->m_cancelled = true; + 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.get()); +} + +PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() +{ + ASSERT_NOT_REACHED(); + return 0; +} + +bool ResourceHandle::supportsBufferedData() +{ + return false; +} + +void ResourceHandle::platformSetDefersLoading(bool defersLoading) +{ + // Initial implementation of this method was required for bug #44157. + + if (d->m_cancelled) + return; + + if (!defersLoading && !d->m_cancellable && d->m_soupRequest.get()) { + d->m_cancellable = adoptGRef(g_cancellable_new()); + webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + return; + } + + // Only supported for http(s) transfers. Something similar would + // probably be needed for data transfers done with GIO. + if (!d->m_soupMessage) + return; + + if (defersLoading) + soup_session_pause_message(defaultSession(), d->m_soupMessage.get()); + else + soup_session_unpause_message(defaultSession(), d->m_soupMessage.get()); +} + +bool ResourceHandle::loadsBlocked() +{ + return false; +} + +bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*) +{ + // Not having this function means that we'll ask the user about re-posting a form + // even when we go back to a page that's still in the cache. + notImplemented(); + return false; +} + +void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials /*storedCredentials*/, ResourceError& error, ResourceResponse& response, Vector<char>& data) +{ + WebCoreSynchronousLoader syncLoader(error, response, data); + // FIXME: we should use the ResourceHandle::create method here, + // but it makes us timeout in a couple of tests. See + // https://bugs.webkit.org/show_bug.cgi?id=41823 + RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, &syncLoader, false /*defersLoading*/, false /*shouldContentSniff*/)); + handle->start(context); + + syncLoader.run(); +} + +static void closeCallback(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(); + + 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 + // bad idea. + if (d->m_cancelled || !client) + return; + + client->didFinishLoading(handle.get(), 0); +} + +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) { + cleanupSoupRequestOperation(handle.get()); + return; + } + + GOwnPtr<GError> error; + + gssize bytesRead = g_input_stream_read_finish(d->m_inputStream.get(), asyncResult, &error.outPtr()); + if (error) { + 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()); + cleanupSoupRequestOperation(handle.get()); + client->didFail(handle.get(), resourceError); + return; + } + + if (!bytesRead) { + g_input_stream_close_async(d->m_inputStream.get(), G_PRIORITY_DEFAULT, + 0, closeCallback, 0); + return; + } + + // It's mandatory to have sent a response before sending data + ASSERT(!d->m_response.isNull()); + + d->m_total += bytesRead; + 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); + } + + // didReceiveData may cancel the load, which may release the last reference. + if (d->m_cancelled || !client) { + 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, data); +} + +static bool startGio(ResourceHandle* handle, KURL url) +{ + ASSERT(handle); + + 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(); + + GOwnPtr<GError> error; + d->m_soupRequest = adoptGRef(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 cleanupSoupRequestOperation, which should always run + handle->ref(); + + d->m_cancellable = adoptGRef(g_cancellable_new()); + webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + + return true; +} + +SoupSession* ResourceHandle::defaultSession() +{ + static SoupSession* session = createSoupSession(); + + return session; +} + +} diff --git a/Source/WebCore/platform/network/soup/ResourceRequest.h b/Source/WebCore/platform/network/soup/ResourceRequest.h new file mode 100644 index 0000000..879a47f --- /dev/null +++ b/Source/WebCore/platform/network/soup/ResourceRequest.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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 COMPUTER, 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 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 ResourceRequest_h +#define ResourceRequest_h + +#include "ResourceRequestBase.h" + +#include <libsoup/soup.h> + +namespace WebCore { + + class ResourceRequest : public ResourceRequestBase { + public: + ResourceRequest(const String& url) + : ResourceRequestBase(KURL(ParsedURLString, url), UseProtocolCachePolicy) + , m_soupFlags(static_cast<SoupMessageFlags>(0)) + { + } + + ResourceRequest(const KURL& url) + : ResourceRequestBase(url, UseProtocolCachePolicy) + , m_soupFlags(static_cast<SoupMessageFlags>(0)) + { + } + + ResourceRequest(const KURL& url, const String& referrer, ResourceRequestCachePolicy policy = UseProtocolCachePolicy) + : ResourceRequestBase(url, policy) + , m_soupFlags(static_cast<SoupMessageFlags>(0)) + { + setHTTPReferrer(referrer); + } + + ResourceRequest() + : ResourceRequestBase(KURL(), UseProtocolCachePolicy) + , m_soupFlags(static_cast<SoupMessageFlags>(0)) + { + } + + ResourceRequest(SoupMessage* soupMessage) + : ResourceRequestBase(KURL(), UseProtocolCachePolicy) + , m_soupFlags(static_cast<SoupMessageFlags>(0)) + { + updateFromSoupMessage(soupMessage); + } + + void updateSoupMessage(SoupMessage*) const; + SoupMessage* toSoupMessage() const; + void updateFromSoupMessage(SoupMessage*); + + SoupMessageFlags soupMessageFlags() const { return m_soupFlags; } + void setSoupMessageFlags(SoupMessageFlags soupFlags) { m_soupFlags = soupFlags; } + + private: + friend class ResourceRequestBase; + + SoupMessageFlags m_soupFlags; + + void doUpdatePlatformRequest() {}; + void doUpdateResourceRequest() {}; + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } + }; + + struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { + }; + +} // namespace WebCore + +#endif // ResourceRequest_h diff --git a/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp b/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp new file mode 100644 index 0000000..d46e47b --- /dev/null +++ b/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2009 Gustavo Noronha Silva + * + * 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; 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 "ResourceRequest.h" + +#include "GOwnPtr.h" +#include "GOwnPtrSoup.h" +#include "HTTPParsers.h" +#include "MIMETypeRegistry.h" +#include "PlatformString.h" +#include "SoupURIUtils.h" +#include <wtf/text/CString.h> + +#include <libsoup/soup.h> + +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()); + if (!soupMessage) + return 0; + + 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); + + // Body data is only handled at ResourceHandleSoup::startHttp for + // now; this is because this may not be a good place to go + // openning and mmapping files. We should maybe revisit this. + return soupMessage; +} + +void ResourceRequest::updateFromSoupMessage(SoupMessage* soupMessage) +{ + m_url = soupURIToKURL(soup_message_get_uri(soupMessage)); + + m_httpMethod = String::fromUTF8(soupMessage->method); + + SoupMessageHeadersIter headersIter; + const char* headerName; + const char* headerValue; + + soup_message_headers_iter_init(&headersIter, soupMessage->request_headers); + while (soup_message_headers_iter_next(&headersIter, &headerName, &headerValue)) + m_httpHeaderFields.set(String::fromUTF8(headerName), String::fromUTF8(headerValue)); + + if (soupMessage->request_body->data) + m_httpBody = FormData::create(soupMessage->request_body->data, soupMessage->request_body->length); + +#ifdef HAVE_LIBSOUP_2_29_90 + SoupURI* firstParty = soup_message_get_first_party(soupMessage); + if (firstParty) + m_firstPartyForCookies = soupURIToKURL(firstParty); +#endif + + m_soupFlags = soup_message_get_flags(soupMessage); + + // FIXME: m_allowCookies should probably be handled here and on + // doUpdatePlatformRequest somehow. +} + +unsigned initializeMaximumHTTPConnectionCountPerHost() +{ + // Soup has its own queue control; it wants to have all requests + // given to it, so that it is able to look ahead, and schedule + // them in a good way. + return 10000; +} + +} diff --git a/Source/WebCore/platform/network/soup/ResourceResponse.h b/Source/WebCore/platform/network/soup/ResourceResponse.h new file mode 100644 index 0000000..e7213f5 --- /dev/null +++ b/Source/WebCore/platform/network/soup/ResourceResponse.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 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 ResourceResponse_h +#define ResourceResponse_h + +#include "ResourceResponseBase.h" + +#include <libsoup/soup.h> + +namespace WebCore { + +class ResourceResponse : public ResourceResponseBase { +public: + ResourceResponse() + : ResourceResponseBase() + , m_soupFlags(static_cast<SoupMessageFlags>(0)) + { + } + + ResourceResponse(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) + : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename) + { + } + + ResourceResponse(SoupMessage* soupMessage) + : ResourceResponseBase() + , m_soupFlags(static_cast<SoupMessageFlags>(0)) + { + updateFromSoupMessage(soupMessage); + } + + SoupMessage* toSoupMessage() const; + void updateFromSoupMessage(SoupMessage* soupMessage); + + SoupMessageFlags soupMessageFlags() const { return m_soupFlags; } + void setSoupMessageFlags(SoupMessageFlags soupFlags) { m_soupFlags = soupFlags; } + +private: + friend class ResourceResponseBase; + + SoupMessageFlags m_soupFlags; + + void doUpdateResourceResponse() { } + + PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { } +}; + +struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { +}; + +} // namespace WebCore + +#endif // ResourceResponse_h diff --git a/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp b/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp new file mode 100644 index 0000000..3bcdcc4 --- /dev/null +++ b/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2009 Gustavo Noronha Silva + * Copyright (C) 2009 Collabora Ltd. + * + * 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; 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 "ResourceResponse.h" + +#include "GOwnPtr.h" +#include "HTTPParsers.h" +#include "MIMETypeRegistry.h" +#include "PlatformString.h" +#include "SoupURIUtils.h" +#include <wtf/text/CString.h> + +using namespace std; + +namespace WebCore { + +SoupMessage* ResourceResponse::toSoupMessage() const +{ + // This GET here is just because SoupMessage wants it, we dn't really know. + SoupMessage* soupMessage = soup_message_new("GET", url().string().utf8().data()); + if (!soupMessage) + return 0; + + soupMessage->status_code = httpStatusCode(); + + const HTTPHeaderMap& headers = httpHeaderFields(); + SoupMessageHeaders* soupHeaders = soupMessage->response_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()); + } + + soup_message_set_flags(soupMessage, m_soupFlags); + + // Body data is not in the message. + return soupMessage; +} + +void ResourceResponse::updateFromSoupMessage(SoupMessage* soupMessage) +{ + m_url = soupURIToKURL(soup_message_get_uri(soupMessage)); + + m_httpStatusCode = soupMessage->status_code; + + SoupMessageHeadersIter headersIter; + const char* headerName; + const char* headerValue; + + soup_message_headers_iter_init(&headersIter, soupMessage->response_headers); + while (soup_message_headers_iter_next(&headersIter, &headerName, &headerValue)) + m_httpHeaderFields.set(String::fromUTF8(headerName), String::fromUTF8(headerValue)); + + m_soupFlags = soup_message_get_flags(soupMessage); + + String contentType = soup_message_headers_get_one(soupMessage->response_headers, "Content-Type"); + setMimeType(extractMIMETypeFromMediaType(contentType)); + + setTextEncodingName(extractCharsetFromMediaType(contentType)); + setExpectedContentLength(soup_message_headers_get_content_length(soupMessage->response_headers)); + setHTTPStatusText(soupMessage->reason_phrase); + setSuggestedFilename(filenameFromHTTPContentDisposition(httpHeaderField("Content-Disposition"))); +} + +} diff --git a/Source/WebCore/platform/network/soup/SocketStreamError.h b/Source/WebCore/platform/network/soup/SocketStreamError.h new file mode 100644 index 0000000..f9641ad --- /dev/null +++ b/Source/WebCore/platform/network/soup/SocketStreamError.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamError_h +#define SocketStreamError_h + +#include "SocketStreamErrorBase.h" + +namespace WebCore { + + class SocketStreamError : public SocketStreamErrorBase { + public: + SocketStreamError() { } + explicit SocketStreamError(int errorCode) + : SocketStreamErrorBase(errorCode) + { + } + + }; + +} // namespace WebCore + +#endif // SocketStreamError_h diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandle.h b/Source/WebCore/platform/network/soup/SocketStreamHandle.h new file mode 100644 index 0000000..3168fae --- /dev/null +++ b/Source/WebCore/platform/network/soup/SocketStreamHandle.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamHandle_h +#define SocketStreamHandle_h + +#include "GRefPtr.h" +#include "SocketStreamHandleBase.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class AuthenticationChallenge; + class Credential; + class SocketStreamHandleClient; + + class SocketStreamHandle : public RefCounted<SocketStreamHandle>, public SocketStreamHandleBase { + public: + static PassRefPtr<SocketStreamHandle> create(const KURL& url, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(url, client)); } + + virtual ~SocketStreamHandle(); + void connected(GSocketConnection*, GError*); + void readBytes(signed long, GError*); + void writeReady(); + void* id() { return m_id; } + + protected: + virtual int platformSend(const char* data, int length); + virtual void platformClose(); + + private: + GRefPtr<GSocketConnection> m_socketConnection; + GRefPtr<GInputStream> m_inputStream; + GRefPtr<GOutputStream> m_outputStream; + GRefPtr<GSource> m_writeReadySource; + char* m_readBuffer; + void* m_id; + + SocketStreamHandle(const KURL&, SocketStreamHandleClient*); + + // No authentication for streams per se, but proxy may ask for credentials. + void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); + void receivedCredential(const AuthenticationChallenge&, const Credential&); + void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&); + void receivedCancellation(const AuthenticationChallenge&); + void beginWaitingForSocketWritability(); + void stopWaitingForSocketWritability(); + }; + +} // namespace WebCore + +#endif // SocketStreamHandle_h diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp b/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp new file mode 100644 index 0000000..841e209 --- /dev/null +++ b/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "SocketStreamHandle.h" + +#include "CString.h" +#include "GOwnPtr.h" +#include "KURL.h" +#include "Logging.h" +#include "NotFound.h" +#include "NotImplemented.h" +#include "SocketStreamError.h" +#include "SocketStreamHandleClient.h" +#include "Vector.h" +#include <gio/gio.h> +#include <glib.h> + +#define READ_BUFFER_SIZE 1024 + +namespace WebCore { + +// These functions immediately call the similarly named SocketStreamHandle methods. +static void connectedCallback(GSocketClient*, GAsyncResult*, void*); +static void readReadyCallback(GInputStream*, GAsyncResult*, void*); +static gboolean writeReadyCallback(GSocket*, GIOCondition, void*); + +// Having a list of active handles means that we do not have to worry about WebCore +// reference counting in GLib callbacks. Once the handle is off the active handles list +// we just ignore it in the callback. We avoid a lot of extra checks and tricky +// situations this way. +static HashMap<void*, SocketStreamHandle*> gActiveHandles; +static SocketStreamHandle* getHandleFromId(void* id) +{ + if (!gActiveHandles.contains(id)) + return 0; + return gActiveHandles.get(id); +} + +static void deactivateHandle(SocketStreamHandle* handle) +{ + gActiveHandles.remove(handle->id()); +} + +static void* activateHandle(SocketStreamHandle* handle) +{ + // The first id cannot be 0, because it conflicts with the HashMap emptyValue. + static gint currentHandleId = 1; + void* id = GINT_TO_POINTER(currentHandleId++); + gActiveHandles.set(id, handle); + return id; +} + +SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient* client) + : SocketStreamHandleBase(url, client) + , m_readBuffer(0) +{ + // No support for SSL sockets yet. + if (url.protocolIs("wss")) + return; + unsigned int port = url.hasPort() ? url.port() : 80; + + m_id = activateHandle(this); + GRefPtr<GSocketClient> socketClient = adoptGRef(g_socket_client_new()); + g_socket_client_connect_to_host_async(socketClient.get(), url.host().utf8().data(), port, 0, + reinterpret_cast<GAsyncReadyCallback>(connectedCallback), m_id); +} + +SocketStreamHandle::~SocketStreamHandle() +{ + // If for some reason we were destroyed without closing, ensure that we are deactivated. + deactivateHandle(this); + setClient(0); +} + +void SocketStreamHandle::connected(GSocketConnection* socketConnection, GError* error) +{ + if (error) { + m_client->didFail(this, SocketStreamError(error->code)); + return; + } + + m_socketConnection = adoptGRef(socketConnection); + m_outputStream = g_io_stream_get_output_stream(G_IO_STREAM(m_socketConnection.get())); + m_inputStream = g_io_stream_get_input_stream(G_IO_STREAM(m_socketConnection.get())); + + m_readBuffer = new char[READ_BUFFER_SIZE]; + g_input_stream_read_async(m_inputStream.get(), m_readBuffer, READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, 0, + reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), m_id); + + // The client can close the handle, potentially removing the last reference. + RefPtr<SocketStreamHandle> protect(this); + m_state = Open; + m_client->didOpen(this); + if (!m_socketConnection) // Client closed the connection. + return; +} + +void SocketStreamHandle::readBytes(signed long bytesRead, GError* error) +{ + if (error) { + m_client->didFail(this, SocketStreamError(error->code)); + return; + } + + if (!bytesRead) { + close(); + return; + } + + // The client can close the handle, potentially removing the last reference. + RefPtr<SocketStreamHandle> protect(this); + m_client->didReceiveData(this, m_readBuffer, bytesRead); + if (m_inputStream) // The client may have closed the connection. + g_input_stream_read_async(m_inputStream.get(), m_readBuffer, READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, 0, + reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), m_id); +} + +void SocketStreamHandle::writeReady() +{ + // We no longer have buffered data, so stop waiting for the socket to be writable. + if (!bufferedAmount()) { + stopWaitingForSocketWritability(); + return; + } + + sendPendingData(); +} + +int SocketStreamHandle::platformSend(const char* data, int length) +{ + if (!g_socket_condition_check(g_socket_connection_get_socket(m_socketConnection.get()), G_IO_OUT)) { + beginWaitingForSocketWritability(); + return 0; + } + + GOwnPtr<GError> error; + gssize written = g_output_stream_write(m_outputStream.get(), data, length, 0, &error.outPtr()); + if (error) { + m_client->didFail(this, SocketStreamError(error->code)); // FIXME: Provide a sensible error. + return 0; + } + + // If we did not send all the bytes we were given, we know that + // SocketStreamHandleBase will need to send more in the future. + if (written < length) + beginWaitingForSocketWritability(); + + return written; +} + +void SocketStreamHandle::platformClose() +{ + // We remove this handle from the active handles list first, to disable all callbacks. + deactivateHandle(this); + stopWaitingForSocketWritability(); + + if (m_socketConnection) { + GOwnPtr<GError> error; + g_io_stream_close(G_IO_STREAM(m_socketConnection.get()), 0, &error.outPtr()); + if (error) + m_client->didFail(this, SocketStreamError(error->code)); // FIXME: Provide a sensible error. + m_socketConnection = 0; + } + + m_outputStream = 0; + m_inputStream = 0; + delete m_readBuffer; + + m_client->didClose(this); +} + +void SocketStreamHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void SocketStreamHandle::beginWaitingForSocketWritability() +{ + if (m_writeReadySource) // Already waiting. + return; + + m_writeReadySource = adoptGRef(g_socket_create_source( + g_socket_connection_get_socket(m_socketConnection.get()), static_cast<GIOCondition>(G_IO_OUT), 0)); + g_source_set_callback(m_writeReadySource.get(), reinterpret_cast<GSourceFunc>(writeReadyCallback), m_id, 0); + g_source_attach(m_writeReadySource.get(), 0); +} + +void SocketStreamHandle::stopWaitingForSocketWritability() +{ + if (!m_writeReadySource) // Not waiting. + return; + + g_source_remove(g_source_get_id(m_writeReadySource.get())); + m_writeReadySource = 0; +} + +static void connectedCallback(GSocketClient* client, GAsyncResult* result, void* id) +{ + // Always finish the connection, even if this SocketStreamHandle was deactivated earlier. + GOwnPtr<GError> error; + GSocketConnection* socketConnection = g_socket_client_connect_to_host_finish(client, result, &error.outPtr()); + + // The SocketStreamHandle has been deactivated, so just close the connection, ignoring errors. + SocketStreamHandle* handle = getHandleFromId(id); + if (!handle) { + g_io_stream_close(G_IO_STREAM(socketConnection), 0, &error.outPtr()); + return; + } + + handle->connected(socketConnection, error.get()); +} + +static void readReadyCallback(GInputStream* stream, GAsyncResult* result, void* id) +{ + // Always finish the read, even if this SocketStreamHandle was deactivated earlier. + GOwnPtr<GError> error; + gssize bytesRead = g_input_stream_read_finish(stream, result, &error.outPtr()); + + SocketStreamHandle* handle = getHandleFromId(id); + if (!handle) + return; + + handle->readBytes(bytesRead, error.get()); +} + +static gboolean writeReadyCallback(GSocket*, GIOCondition condition, void* id) +{ + SocketStreamHandle* handle = getHandleFromId(id); + if (!handle) + return FALSE; + + // G_IO_HUP and G_IO_ERR are are always active. See: + // http://library.gnome.org/devel/gio/stable/GSocket.html#g-socket-create-source + if (condition & G_IO_HUP) { + handle->close(); + return FALSE; + } + if (condition & G_IO_ERR) { + handle->client()->didFail(handle, SocketStreamError(0)); // FIXME: Provide a sensible error. + return FALSE; + } + if (condition & G_IO_OUT) + handle->writeReady(); + return TRUE; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/soup/SoupURIUtils.cpp b/Source/WebCore/platform/network/soup/SoupURIUtils.cpp new file mode 100644 index 0000000..1f65615 --- /dev/null +++ b/Source/WebCore/platform/network/soup/SoupURIUtils.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 20010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "SoupURIUtils.h" + +#include "GOwnPtr.h" +#include <libsoup/soup.h> + +namespace WebCore { + +// Motivated by https://bugs.webkit.org/show_bug.cgi?id=38956. libsoup +// does not add the password to the URL when calling +// soup_uri_to_string, and thus the requests are not properly +// built. Fixing soup_uri_to_string is a no-no as the maintainer does +// not want to break compatibility with previous implementations +KURL soupURIToKURL(SoupURI* soupURI) +{ + GOwnPtr<gchar> urlString(soup_uri_to_string(soupURI, FALSE)); + KURL url(KURL(), String::fromUTF8(urlString.get())); + + if (!soupURI->password) + return url; + + url.setPass(String::fromUTF8(soupURI->password)); + return url; +} + +} diff --git a/Source/WebCore/platform/network/soup/SoupURIUtils.h b/Source/WebCore/platform/network/soup/SoupURIUtils.h new file mode 100644 index 0000000..69772e7 --- /dev/null +++ b/Source/WebCore/platform/network/soup/SoupURIUtils.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * 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 COMPUTER, 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 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 SoupURIUtils_h +#define SoupURIUtils_h + +#include "KURL.h" + +typedef struct _SoupURI SoupURI; + +namespace WebCore { +KURL soupURIToKURL(SoupURI* soupURI); +} + +#endif diff --git a/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.c b/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.c new file mode 100644 index 0000000..c14863b --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.h b/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.h new file mode 100644 index 0000000..0c5b0be --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.c b/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.c new file mode 100644 index 0000000..195c458 --- /dev/null +++ b/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.c @@ -0,0 +1,922 @@ +/* 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); + g_object_unref (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/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.h b/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.h new file mode 100644 index 0000000..6b98559 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/soup-request-data.c b/Source/WebCore/platform/network/soup/cache/soup-request-data.c new file mode 100644 index 0000000..ced5c4a --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/soup-request-data.h b/Source/WebCore/platform/network/soup/cache/soup-request-data.h new file mode 100644 index 0000000..c9631a4 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/soup-request-file.c b/Source/WebCore/platform/network/soup/cache/soup-request-file.c new file mode 100644 index 0000000..24ccb10 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/soup-request-file.h b/Source/WebCore/platform/network/soup/cache/soup-request-file.h new file mode 100644 index 0000000..459e82a --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/soup-request-http.c b/Source/WebCore/platform/network/soup/cache/soup-request-http.c new file mode 100644 index 0000000..777fd72 --- /dev/null +++ b/Source/WebCore/platform/network/soup/cache/soup-request-http.c @@ -0,0 +1,356 @@ +/* -*- 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); + + /* FIXME: Uncomment this when this becomes part of libsoup + * if (!soup_message_disables_feature(helper->original, SOUP_TYPE_CONTENT_SNIFFER)) { + * const gchar *content_type = soup_message_headers_get_content_type (msg->response_headers, NULL); + * soup_message_content_sniffed (helper->original, content_type, NULL); + * } + */ + 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; + WebKitSoupHTTPInputStream *httpstream; +} 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; + SendAsyncHelper *helper = (SendAsyncHelper *)data; + 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, helper->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); + + /* FIXME: Uncomment this when this becomes part of libsoup + * if (!soup_message_disables_feature(helper->http->priv->msg, SOUP_TYPE_CONTENT_SNIFFER)) { + * const gchar *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); + * } + */ + 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_object_unref (helper->http); + 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) { + WebKitSoupHTTPInputStream *httpstream; + + httpstream = (WebKitSoupHTTPInputStream *) + webkit_soup_cache_send_response (cache, SOUP_MESSAGE (http->priv->msg)); + + /* Cached resource file could have been deleted outside + */ + if (httpstream) { + /* 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 = g_object_ref (http); + helper->callback = callback; + helper->user_data = user_data; + helper->httpstream = httpstream; + 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/Source/WebCore/platform/network/soup/cache/soup-request-http.h b/Source/WebCore/platform/network/soup/cache/soup-request-http.h new file mode 100644 index 0000000..a06a821 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/soup-request.c b/Source/WebCore/platform/network/soup/cache/soup-request.c new file mode 100644 index 0000000..46b9f5a --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/soup-request.h b/Source/WebCore/platform/network/soup/cache/soup-request.h new file mode 100644 index 0000000..837d8f4 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/soup-requester.c b/Source/WebCore/platform/network/soup/cache/soup-requester.c new file mode 100644 index 0000000..4d8a860 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/soup-requester.h b/Source/WebCore/platform/network/soup/cache/soup-requester.h new file mode 100644 index 0000000..71ff103 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h b/Source/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h new file mode 100644 index 0000000..8af8de2 --- /dev/null +++ b/Source/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/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.c b/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.c new file mode 100644 index 0000000..b96428d --- /dev/null +++ b/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.c @@ -0,0 +1,1677 @@ +/* -*- 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; + } + + soup_header_free_param_list (hash); + } + + 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; + + cache_control = soup_message_headers_get (entry->headers, "Cache-Control"); + if (cache_control) { + const char *max_age, *s_maxage; + gint64 freshness_lifetime = 0; + GHashTable *hash; + 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; + } + + 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 (!entry->dirty); + 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); + + /* It's possible to get a CACHE_VALIDATES with no + * entry in the hash table. This could happen if for + * example the soup client is the one creating the + * conditional request. + */ + if (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_free (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 data, + gpointer user_data) +{ + WebKitSoupCache *cache = (WebKitSoupCache *) user_data; + WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data; + + 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; + GList *entries; + + priv = WEBKIT_SOUP_CACHE (object)->priv; + + // Cannot use g_hash_table_foreach as callbacks must not modify the hash table + entries = g_hash_table_get_values (priv->cache); + g_list_foreach (entries, remove_cache_item, object); + g_list_free (entries); + + 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; + 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); + g_free (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 request is a conditional request issued by the client. + */ + if (soup_message_headers_get (msg->request_headers, "If-Modified-Since") || + soup_message_headers_get (msg->request_headers, "If-None-Match")) + return WEBKIT_SOUP_CACHE_RESPONSE_STALE; + + /* 5. 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) { + GHashTable *hash = soup_header_parse_param_list (cache_control); + + if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) { + soup_header_free_param_list (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); + + soup_header_free_param_list (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; + } + } + + /* 6. 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 data, + gpointer user_data) +{ + WebKitSoupCache *cache = (WebKitSoupCache *) user_data; + WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data; + + 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; + GList *entries; + + g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache)); + + hash = cache->priv->cache; + g_return_if_fail (hash); + + // Cannot use g_hash_table_foreach as callbacks must not modify the hash table + entries = g_hash_table_get_values (hash); + g_list_foreach (entries, clear_cache_item, cache); + g_list_free (entries); +} + +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); + g_free (contents); + 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, g_free, contents); + 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/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.h b/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.h new file mode 100644 index 0000000..a926f98 --- /dev/null +++ b/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.h @@ -0,0 +1,106 @@ +/* -*- 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 + +#ifdef G_OS_WIN32 + #ifdef BUILDING_WEBKIT + #define WEBKIT_API __declspec(dllexport) + #else + #define WEBKIT_API __declspec(dllimport) + #endif + #define WEBKIT_OBSOLETE_API WEBKIT_API +#else + #define WEBKIT_API __attribute__((visibility("default"))) + #define WEBKIT_OBSOLETE_API WEBKIT_API __attribute__((deprecated)) +#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/Source/WebCore/platform/network/win/AuthenticationChallenge.h b/Source/WebCore/platform/network/win/AuthenticationChallenge.h new file mode 100644 index 0000000..7463539 --- /dev/null +++ b/Source/WebCore/platform/network/win/AuthenticationChallenge.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2007 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 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 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 AuthenticationChallenge_h +#define AuthenticationChallenge_h + +#include "AuthenticationChallengeBase.h" + +namespace WebCore { + +class AuthenticationChallenge : public AuthenticationChallengeBase { +public: + AuthenticationChallenge() + { + } + + AuthenticationChallenge(const ProtectionSpace& protectionSpace, const Credential& proposedCredential, unsigned previousFailureCount, const ResourceResponse& response, const ResourceError& error) + : AuthenticationChallengeBase(protectionSpace, proposedCredential, previousFailureCount, response, error) + { + } +}; + +} // namespace WebCore + +#endif // AuthenticationChallenge_h diff --git a/Source/WebCore/platform/network/win/CookieJarWin.cpp b/Source/WebCore/platform/network/win/CookieJarWin.cpp new file mode 100644 index 0000000..a2afbc6 --- /dev/null +++ b/Source/WebCore/platform/network/win/CookieJarWin.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2006, 2007, 2008 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 COMPUTER, 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 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 "CookieJar.h" + +#include "Cookie.h" +#include "KURL.h" +#include "PlatformString.h" +#include "Document.h" +#include "ResourceHandle.h" +#include <windows.h> +#include <Wininet.h> + +namespace WebCore { + +void setCookies(Document* /*document*/, const KURL& url, const String& value) +{ + // FIXME: Deal with document->firstPartyForCookies(). + String str = url.string(); + String val = value; + InternetSetCookie(str.charactersWithNullTermination(), 0, val.charactersWithNullTermination()); +} + +String cookies(const Document* /*document*/, const KURL& url) +{ + String str = url.string(); + + 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); + if (!InternetGetCookie(str.charactersWithNullTermination(), 0, buffer.data(), &count)) + return String(); + + buffer.shrink(count - 1); // Ignore the null terminator. + return String::adopt(buffer); +} + +String cookieRequestHeaderFieldValue(const Document* document, const KURL& url) +{ + // FIXME: include HttpOnly cookie + return cookies(document, url); +} + +bool cookiesEnabled(const Document* /*document*/) +{ + return true; +} + +bool getRawCookies(const Document*, const KURL&, Vector<Cookie>& rawCookies) +{ + // FIXME: Not yet implemented + rawCookies.clear(); + return false; // return true when implemented +} + +void deleteCookie(const Document*, const KURL&, const String&) +{ + // FIXME: Not yet implemented +} + +} diff --git a/Source/WebCore/platform/network/win/NetworkStateNotifierWin.cpp b/Source/WebCore/platform/network/win/NetworkStateNotifierWin.cpp new file mode 100644 index 0000000..e600e41 --- /dev/null +++ b/Source/WebCore/platform/network/win/NetworkStateNotifierWin.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2008 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. ``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. + */ + +#include "config.h" +#include "NetworkStateNotifier.h" + +#include <wtf/MainThread.h> +#include <wtf/Vector.h> + +#include <winsock2.h> +#include <iphlpapi.h> + +namespace WebCore { + +void NetworkStateNotifier::updateState() +{ + // Assume that we're online until proven otherwise. + m_isOnLine = true; + + Vector<char> buffer; + DWORD size = 0; + + if (::GetAdaptersAddresses(AF_UNSPEC, 0, 0, 0, &size) != ERROR_BUFFER_OVERFLOW)
+ return;
+
+ buffer.resize(size);
+ PIP_ADAPTER_ADDRESSES addresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buffer.data());
+ + if (::GetAdaptersAddresses(AF_UNSPEC, 0, 0, addresses, &size) != ERROR_SUCCESS) { + // We couldn't determine whether we're online or not, so assume that we are. + return; + } + + for (; addresses; addresses = addresses->Next) {
+ if (addresses->IfType == MIB_IF_TYPE_LOOPBACK)
+ continue;
+
+ if (addresses->OperStatus != IfOperStatusUp)
+ continue;
+
+ // We found an interface that was up.
+ return;
+ } + + // We didn't find any valid interfaces, so we must be offline. + m_isOnLine = false; +} + +void NetworkStateNotifier::addressChanged() +{ + bool oldOnLine = m_isOnLine; + + updateState(); + + if (m_isOnLine == oldOnLine) + return; + + if (m_networkStateChangedFunction) + m_networkStateChangedFunction(); +} + +void NetworkStateNotifier::callAddressChanged(void* context) +{ + static_cast<NetworkStateNotifier*>(context)->addressChanged(); +} + +void CALLBACK NetworkStateNotifier::addrChangeCallback(void* context, BOOLEAN timedOut) +{ + // NotifyAddrChange only notifies us of a single address change. Now that we've been notified, + // we need to call it again so we'll get notified the *next* time. + static_cast<NetworkStateNotifier*>(context)->registerForAddressChange(); + + callOnMainThread(callAddressChanged, context); +} + +void NetworkStateNotifier::registerForAddressChange() +{ + HANDLE handle; + ::NotifyAddrChange(&handle, &m_overlapped); +} + +NetworkStateNotifier::NetworkStateNotifier() + : m_isOnLine(false) + , m_networkStateChangedFunction(0) +{ + updateState(); + + memset(&m_overlapped, 0, sizeof(m_overlapped)); + +// FIXME: Check m_overlapped on WinCE. +#if !OS(WINCE) + m_overlapped.hEvent = ::CreateEvent(0, false, false, 0); + + ::RegisterWaitForSingleObject(&m_waitHandle, m_overlapped.hEvent, addrChangeCallback, this, INFINITE, 0); + + registerForAddressChange(); +#endif +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/win/ResourceError.h b/Source/WebCore/platform/network/win/ResourceError.h new file mode 100644 index 0000000..a09c227 --- /dev/null +++ b/Source/WebCore/platform/network/win/ResourceError.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 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 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 ResourceError_h +#define ResourceError_h + +#include "ResourceErrorBase.h" + +namespace WebCore { + +class ResourceError : public ResourceErrorBase { +public: + ResourceError() + { + } + + ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) + : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) + { + } +}; + +} // namespace WebCore + +#endif // ResourceError_h diff --git a/Source/WebCore/platform/network/win/ResourceHandleWin.cpp b/Source/WebCore/platform/network/win/ResourceHandleWin.cpp new file mode 100644 index 0000000..38d9cd1 --- /dev/null +++ b/Source/WebCore/platform/network/win/ResourceHandleWin.cpp @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * 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 COMPUTER, 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 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 "ResourceHandle.h" + +#include "DataURL.h" +#include "HTTPParsers.h" +#include "MIMETypeRegistry.h" +#include "MainThread.h" +#include "NotImplemented.h" +#include "ResourceError.h" +#include "ResourceHandleClient.h" +#include "ResourceHandleInternal.h" +#include "SharedBuffer.h" +#include "Timer.h" +#include "UnusedParam.h" +#include <wtf/text/CString.h> +#include <windows.h> +#include <wininet.h> + +namespace WebCore { + +static inline HINTERNET createInternetHandle(const String& userAgent, bool asynchronous) +{ + String userAgentString = userAgent; + HINTERNET internetHandle = InternetOpenW(userAgentString.charactersWithNullTermination(), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, asynchronous ? INTERNET_FLAG_ASYNC : 0); + + if (asynchronous) + InternetSetStatusCallback(internetHandle, &ResourceHandle::internetStatusCallback); + + return internetHandle; +} + +static HINTERNET asynchronousInternetHandle(const String& userAgent) +{ + static HINTERNET internetHandle = createInternetHandle(userAgent, true); + return internetHandle; +} + +static String queryHTTPHeader(HINTERNET requestHandle, DWORD infoLevel) +{ + DWORD bufferSize = 0; + HttpQueryInfoW(requestHandle, infoLevel, 0, &bufferSize, 0); + + Vector<UChar> characters(bufferSize / sizeof(UChar)); + + if (!HttpQueryInfoW(requestHandle, infoLevel, characters.data(), &bufferSize, 0)) + return String(); + + characters.removeLast(); // Remove NullTermination. + return String::adopt(characters); +} + + +class WebCoreSynchronousLoader : public ResourceHandleClient, public Noncopyable { +public: + WebCoreSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&, const String& userAgent); + ~WebCoreSynchronousLoader(); + + HINTERNET internetHandle() const { return m_internetHandle; } + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); + virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/); + virtual void didFail(ResourceHandle*, const ResourceError&); + +private: + ResourceError& m_error; + ResourceResponse& m_response; + Vector<char>& m_data; + HINTERNET m_internetHandle; +}; + +WebCoreSynchronousLoader::WebCoreSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data, const String& userAgent) + : m_error(error) + , m_response(response) + , m_data(data) + , m_internetHandle(createInternetHandle(userAgent, false)) +{ +} + +WebCoreSynchronousLoader::~WebCoreSynchronousLoader() +{ + InternetCloseHandle(m_internetHandle); +} + +void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) +{ + m_response = response; +} + +void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int) +{ + m_data.append(data, length); +} + +void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*, double) +{ +} + +void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error) +{ + m_error = error; +} + + +ResourceHandleInternal::~ResourceHandleInternal() +{ +} + +ResourceHandle::~ResourceHandle() +{ +} + +static void callOnRedirect(void* context) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(context); + handle->onRedirect(); +} + +static void callOnRequestComplete(void* context) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(context); + handle->onRequestComplete(); +} + +void ResourceHandle::internetStatusCallback(HINTERNET internetHandle, DWORD_PTR context, DWORD internetStatus, + LPVOID statusInformation, DWORD statusInformationLength) +{ + ResourceHandle* handle = reinterpret_cast<ResourceHandle*>(context); + + switch (internetStatus) { + case INTERNET_STATUS_REDIRECT: + handle->d->m_redirectUrl = String(static_cast<UChar*>(statusInformation), statusInformationLength); + callOnMainThread(callOnRedirect, handle); + break; + + case INTERNET_STATUS_REQUEST_COMPLETE: + callOnMainThread(callOnRequestComplete, handle); + break; + + default: + break; + } +} + +void ResourceHandle::onRedirect() +{ + ResourceRequest newRequest = firstRequest(); + newRequest.setURL(KURL(ParsedURLString, d->m_redirectUrl)); + + ResourceResponse response(firstRequest().url(), String(), 0, String(), String()); + + if (ResourceHandleClient* resourceHandleClient = client()) + resourceHandleClient->willSendRequest(this, newRequest, response); +} + +bool ResourceHandle::onRequestComplete() +{ + if (!d->m_internetHandle) { // 0 if canceled. + deref(); // balances ref in start + return false; + } + + if (d->m_bytesRemainingToWrite) { + DWORD bytesWritten; + InternetWriteFile(d->m_requestHandle, + d->m_formData.data() + (d->m_formData.size() - d->m_bytesRemainingToWrite), + d->m_bytesRemainingToWrite, + &bytesWritten); + d->m_bytesRemainingToWrite -= bytesWritten; + if (d->m_bytesRemainingToWrite) + return true; + d->m_formData.clear(); + } + + if (!d->m_sentEndRequest) { + HttpEndRequestW(d->m_requestHandle, 0, 0, reinterpret_cast<DWORD_PTR>(this)); + d->m_sentEndRequest = true; + return true; + } + + static const int bufferSize = 32768; + char buffer[bufferSize]; + INTERNET_BUFFERSA buffers; + buffers.dwStructSize = sizeof(INTERNET_BUFFERSA); + buffers.lpvBuffer = buffer; + buffers.dwBufferLength = bufferSize; + + BOOL ok = FALSE; + while ((ok = InternetReadFileExA(d->m_requestHandle, &buffers, d->m_loadSynchronously ? 0 : IRF_NO_WAIT, reinterpret_cast<DWORD_PTR>(this))) && buffers.dwBufferLength) { + if (!d->m_hasReceivedResponse) { + d->m_hasReceivedResponse = true; + + ResourceResponse response; + response.setURL(firstRequest().url()); + + String httpStatusText = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_STATUS_TEXT); + if (!httpStatusText.isNull()) + response.setHTTPStatusText(httpStatusText); + + String httpStatusCode = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_STATUS_CODE); + if (!httpStatusCode.isNull()) + response.setHTTPStatusCode(httpStatusCode.toInt()); + + String httpContentLength = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_CONTENT_LENGTH); + if (!httpContentLength.isNull()) + response.setExpectedContentLength(httpContentLength.toInt()); + + String httpContentType = queryHTTPHeader(d->m_requestHandle, HTTP_QUERY_CONTENT_TYPE); + if (!httpContentType.isNull()) { + response.setMimeType(extractMIMETypeFromMediaType(httpContentType)); + response.setTextEncodingName(extractCharsetFromMediaType(httpContentType)); + } + + if (ResourceHandleClient* resourceHandleClient = client()) + resourceHandleClient->didReceiveResponse(this, response); + } + + if (ResourceHandleClient* resourceHandleClient = client()) + resourceHandleClient->didReceiveData(this, buffer, buffers.dwBufferLength, 0); + buffers.dwBufferLength = bufferSize; + } + + if (!ok && GetLastError() == ERROR_IO_PENDING) + return true; + + if (ResourceHandleClient* resourceHandleClient = client()) + resourceHandleClient->didFinishLoading(this, 0); + + InternetCloseHandle(d->m_requestHandle); + InternetCloseHandle(d->m_connectHandle); + deref(); // balances ref in start + return false; +} + +bool ResourceHandle::start(NetworkingContext* context) +{ + if (firstRequest().url().isLocalFile() || firstRequest().url().protocolIsData()) { + ref(); // balanced by deref in fileLoadTimer + if (d->m_loadSynchronously) + fileLoadTimer(0); + else + d->m_fileLoadTimer.startOneShot(0.0); + return true; + } + + if (!d->m_internetHandle) + d->m_internetHandle = asynchronousInternetHandle(context->userAgent()); + + if (!d->m_internetHandle) + return false; + + DWORD flags = INTERNET_FLAG_KEEP_CONNECTION + | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS + | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP + | INTERNET_FLAG_DONT_CACHE + | INTERNET_FLAG_RELOAD; + + d->m_connectHandle = InternetConnectW(d->m_internetHandle, firstRequest().url().host().charactersWithNullTermination(), firstRequest().url().port(), + 0, 0, INTERNET_SERVICE_HTTP, flags, reinterpret_cast<DWORD_PTR>(this)); + + if (!d->m_connectHandle) + return false; + + String urlStr = firstRequest().url().path(); + String urlQuery = firstRequest().url().query(); + + if (!urlQuery.isEmpty()) { + urlStr.append('?'); + urlStr.append(urlQuery); + } + + String httpMethod = firstRequest().httpMethod(); + String httpReferrer = firstRequest().httpReferrer(); + + LPCWSTR httpAccept[] = { L"*/*", 0 }; + + d->m_requestHandle = HttpOpenRequestW(d->m_connectHandle, httpMethod.charactersWithNullTermination(), urlStr.charactersWithNullTermination(), + 0, httpReferrer.charactersWithNullTermination(), httpAccept, flags, reinterpret_cast<DWORD_PTR>(this)); + + if (!d->m_requestHandle) { + InternetCloseHandle(d->m_connectHandle); + return false; + } + + if (firstRequest().httpBody()) { + firstRequest().httpBody()->flatten(d->m_formData); + d->m_bytesRemainingToWrite = d->m_formData.size(); + } + + Vector<UChar> httpHeaders; + const HTTPHeaderMap& httpHeaderFields = firstRequest().httpHeaderFields(); + + for (HTTPHeaderMap::const_iterator it = httpHeaderFields.begin(); it != httpHeaderFields.end(); ++it) { + if (equalIgnoringCase(it->first, "Accept") || equalIgnoringCase(it->first, "Referer") || equalIgnoringCase(it->first, "User-Agent")) + continue; + + if (!httpHeaders.isEmpty()) + httpHeaders.append('\n'); + + httpHeaders.append(it->first.characters(), it->first.length()); + httpHeaders.append(':'); + httpHeaders.append(it->second.characters(), it->second.length()); + } + + INTERNET_BUFFERSW internetBuffers; + ZeroMemory(&internetBuffers, sizeof(internetBuffers)); + internetBuffers.dwStructSize = sizeof(internetBuffers); + internetBuffers.lpcszHeader = httpHeaders.data(); + internetBuffers.dwHeadersLength = httpHeaders.size(); + internetBuffers.dwBufferTotal = d->m_bytesRemainingToWrite; + + HttpSendRequestExW(d->m_requestHandle, &internetBuffers, 0, 0, reinterpret_cast<DWORD_PTR>(this)); + + ref(); // balanced by deref in onRequestComplete + + if (d->m_loadSynchronously) + while (onRequestComplete()) { + // Loop until finished. + } + + return true; +} + +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); + + if (fileHandle == INVALID_HANDLE_VALUE) { + client()->didFail(this, ResourceError()); + return; + } + + ResourceResponse response; + + int dotPos = fileName.reverseFind('.'); + int slashPos = fileName.reverseFind('/'); + + if (slashPos < dotPos && dotPos != -1) { + String ext = fileName.substring(dotPos + 1); + response.setMimeType(MIMETypeRegistry::getMIMETypeForExtension(ext)); + } + + client()->didReceiveResponse(this, response); + + bool result = false; + DWORD bytesRead = 0; + + do { + const int bufferSize = 8192; + char buffer[bufferSize]; + result = ReadFile(fileHandle, &buffer, bufferSize, &bytesRead, 0); + if (result && bytesRead) + client()->didReceiveData(this, buffer, bytesRead, 0); + // Check for end of file. + } while (result && bytesRead); + + CloseHandle(fileHandle); + + client()->didFinishLoading(this, 0); +} + +void ResourceHandle::cancel() +{ + if (d->m_requestHandle) { + d->m_internetHandle = 0; + InternetCloseHandle(d->m_requestHandle); + InternetCloseHandle(d->m_connectHandle); + } else + d->m_fileLoadTimer.stop(); +} + +void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) +{ + UNUSED_PARAM(storedCredentials); + + WebCoreSynchronousLoader syncLoader(error, response, data, request.httpUserAgent()); + ResourceHandle handle(request, &syncLoader, true, false); + + handle.setSynchronousInternetHandle(syncLoader.internetHandle()); + handle.start(context); +} + +void ResourceHandle::setSynchronousInternetHandle(HINTERNET internetHandle) +{ + d->m_internetHandle = internetHandle; + d->m_loadSynchronously = true; +} + +bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*) +{ + notImplemented(); + return false; +} + +void prefetchDNS(const String&) +{ + notImplemented(); +} + +PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() +{ + ASSERT_NOT_REACHED(); + return 0; +} + +bool ResourceHandle::supportsBufferedData() +{ + return false; +} + +bool ResourceHandle::loadsBlocked() +{ + return false; +} + +void ResourceHandle::platformSetDefersLoading(bool) +{ + notImplemented(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/win/ResourceRequest.h b/Source/WebCore/platform/network/win/ResourceRequest.h new file mode 100644 index 0000000..cd50311 --- /dev/null +++ b/Source/WebCore/platform/network/win/ResourceRequest.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.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 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 ResourceRequest_h +#define ResourceRequest_h + +#include "ResourceRequestBase.h" + +namespace WebCore { + +class ResourceRequest : public ResourceRequestBase { +public: + ResourceRequest(const String& url) + : ResourceRequestBase(KURL(ParsedURLString, url), UseProtocolCachePolicy) + { + } + + ResourceRequest(const KURL& url) + : ResourceRequestBase(url, UseProtocolCachePolicy) + { + } + + ResourceRequest(const KURL& url, const String& referrer, ResourceRequestCachePolicy policy = UseProtocolCachePolicy) + : ResourceRequestBase(url, policy) + { + setHTTPReferrer(referrer); + } + + ResourceRequest() + : ResourceRequestBase(KURL(), UseProtocolCachePolicy) + { + } + +private: + friend struct ResourceRequestBase; + + void doUpdatePlatformRequest() { } + void doUpdateResourceRequest() { } + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } +}; + +struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { +}; + +} // namespace WebCore + +#endif // ResourceRequest_h diff --git a/Source/WebCore/platform/network/win/ResourceResponse.h b/Source/WebCore/platform/network/win/ResourceResponse.h new file mode 100644 index 0000000..b74a1e8 --- /dev/null +++ b/Source/WebCore/platform/network/win/ResourceResponse.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2006 Apple Computer, 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 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 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 ResourceResponse_h +#define ResourceResponse_h + +#include "ResourceResponseBase.h" + +namespace WebCore { + +class ResourceResponse : public ResourceResponseBase { +public: + ResourceResponse() + { + } + + ResourceResponse(const KURL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) + : 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 + +#endif // ResourceResponse_h diff --git a/Source/WebCore/platform/network/win/SocketStreamError.h b/Source/WebCore/platform/network/win/SocketStreamError.h new file mode 100644 index 0000000..80dfa39 --- /dev/null +++ b/Source/WebCore/platform/network/win/SocketStreamError.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamError_h +#define SocketStreamError_h + +#include "SocketStreamErrorBase.h" + +namespace WebCore { + +class SocketStreamError : public SocketStreamErrorBase { +public: + SocketStreamError() { } + explicit SocketStreamError(int errorCode) + : SocketStreamErrorBase(errorCode) + { + } + +}; + +} // namespace WebCore + +#endif // SocketStreamError_h diff --git a/Source/WebCore/platform/network/win/SocketStreamHandle.h b/Source/WebCore/platform/network/win/SocketStreamHandle.h new file mode 100644 index 0000000..62808ec --- /dev/null +++ b/Source/WebCore/platform/network/win/SocketStreamHandle.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 SocketStreamHandle_h +#define SocketStreamHandle_h + +#include "SocketStreamHandleBase.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class AuthenticationChallenge; +class Credential; +class SocketStreamHandleClient; + +class SocketStreamHandle : public RefCounted<SocketStreamHandle>, public SocketStreamHandleBase { +public: + static PassRefPtr<SocketStreamHandle> create(const KURL& url, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(url, client)); } + + virtual ~SocketStreamHandle(); + +protected: + virtual int platformSend(const char* data, int length); + virtual void platformClose(); + +private: + SocketStreamHandle(const KURL&, SocketStreamHandleClient*); + + // No authentication for streams per se, but proxy may ask for credentials. + void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); + void receivedCredential(const AuthenticationChallenge&, const Credential&); + void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&); + void receivedCancellation(const AuthenticationChallenge&); +}; + +} // namespace WebCore + +#endif // SocketStreamHandle_h diff --git a/Source/WebCore/platform/network/win/SocketStreamHandleWin.cpp b/Source/WebCore/platform/network/win/SocketStreamHandleWin.cpp new file mode 100644 index 0000000..b2b540f --- /dev/null +++ b/Source/WebCore/platform/network/win/SocketStreamHandleWin.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009 Brent Fulgham. All rights reserved. + * Copyright (C) 2009 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "SocketStreamHandle.h" + +#include "KURL.h" +#include "Logging.h" +#include "NotImplemented.h" +#include "SocketStreamHandleClient.h" + +namespace WebCore { + +SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient* client) + : SocketStreamHandleBase(url, client) +{ + LOG(Network, "SocketStreamHandle %p new client %p", this, m_client); + notImplemented(); +} + +SocketStreamHandle::~SocketStreamHandle() +{ + LOG(Network, "SocketStreamHandle %p delete", this); + setClient(0); + notImplemented(); +} + +int SocketStreamHandle::platformSend(const char*, int) +{ + LOG(Network, "SocketStreamHandle %p platformSend", this); + notImplemented(); + return 0; +} + +void SocketStreamHandle::platformClose() +{ + LOG(Network, "SocketStreamHandle %p platformClose", this); + notImplemented(); +} + +void SocketStreamHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) +{ + notImplemented(); +} + +void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&) +{ + notImplemented(); +} + +} // namespace WebCore |
