diff options
Diffstat (limited to 'Source/WebCore/platform/network')
115 files changed, 7152 insertions, 4698 deletions
diff --git a/Source/WebCore/platform/network/AuthenticationChallengeBase.cpp b/Source/WebCore/platform/network/AuthenticationChallengeBase.cpp index 77bfccd33..7c19e1f52 100644 --- a/Source/WebCore/platform/network/AuthenticationChallengeBase.cpp +++ b/Source/WebCore/platform/network/AuthenticationChallengeBase.cpp @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 diff --git a/Source/WebCore/platform/network/AuthenticationChallengeBase.h b/Source/WebCore/platform/network/AuthenticationChallengeBase.h index 6afb844cd..c1fed1375 100644 --- a/Source/WebCore/platform/network/AuthenticationChallengeBase.h +++ b/Source/WebCore/platform/network/AuthenticationChallengeBase.h @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -36,19 +36,19 @@ class AuthenticationChallenge; class AuthenticationChallengeBase { public: - AuthenticationChallengeBase(); + WEBCORE_EXPORT 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; + WEBCORE_EXPORT unsigned previousFailureCount() const; + WEBCORE_EXPORT const Credential& proposedCredential() const; + WEBCORE_EXPORT const ProtectionSpace& protectionSpace() const; + WEBCORE_EXPORT const ResourceResponse& failureResponse() const; + WEBCORE_EXPORT const ResourceError& error() const; - bool isNull() const; - void nullify(); + WEBCORE_EXPORT bool isNull() const; + WEBCORE_EXPORT void nullify(); - static bool compare(const AuthenticationChallenge& a, const AuthenticationChallenge& b); + WEBCORE_EXPORT static bool compare(const AuthenticationChallenge& a, const AuthenticationChallenge& b); protected: // The AuthenticationChallenge subclass may "shadow" this method to compare platform specific fields diff --git a/Source/WebCore/platform/network/AuthenticationClient.h b/Source/WebCore/platform/network/AuthenticationClient.h index 1e17910e2..8dec55d47 100644 --- a/Source/WebCore/platform/network/AuthenticationClient.h +++ b/Source/WebCore/platform/network/AuthenticationClient.h @@ -36,6 +36,8 @@ public: virtual void receivedCredential(const AuthenticationChallenge&, const Credential&) = 0; virtual void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) = 0; virtual void receivedCancellation(const AuthenticationChallenge&) = 0; + virtual void receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&) = 0; + virtual void receivedChallengeRejection(const AuthenticationChallenge&) = 0; void ref() { refAuthenticationClient(); } void deref() { derefAuthenticationClient(); } diff --git a/Source/WebCore/platform/network/BlobData.cpp b/Source/WebCore/platform/network/BlobData.cpp index 39f7126fa..38cb4d172 100644 --- a/Source/WebCore/platform/network/BlobData.cpp +++ b/Source/WebCore/platform/network/BlobData.cpp @@ -33,65 +33,54 @@ #include "Blob.h" #include "BlobURL.h" -#include "ThreadableBlobRegistry.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; - -RawData::RawData() -{ -} - -void RawData::detachFromCurrentThread() +BlobData::BlobData(const String& contentType) + : m_contentType(contentType) { + ASSERT(Blob::isNormalizedContentType(contentType)); } -void BlobDataItem::detachFromCurrentThread() -{ - data->detachFromCurrentThread(); - path = path.isolatedCopy(); - url = url.copy(); -} +const long long BlobDataItem::toEndOfFile = -1; -void BlobData::detachFromCurrentThread() +long long BlobDataItem::length() const { - m_contentType = m_contentType.isolatedCopy(); - m_contentDisposition = m_contentDisposition.isolatedCopy(); - for (size_t i = 0; i < m_items.size(); ++i) - m_items.at(i).detachFromCurrentThread(); + if (m_length != toEndOfFile) + return m_length; + + switch (m_type) { + case Type::Data: + ASSERT_NOT_REACHED(); + return m_length; + case Type::File: + return m_file->size(); + } + + ASSERT_NOT_REACHED(); + return m_length; } -void BlobData::setContentType(const String& contentType) +void BlobData::appendData(const ThreadSafeDataBuffer& data) { - ASSERT(Blob::isNormalizedContentType(contentType)); - m_contentType = contentType; + size_t dataSize = data.data() ? data.data()->size() : 0; + appendData(data, 0, dataSize); } -void BlobData::appendData(PassRefPtr<RawData> data, long long offset, long long length) +void BlobData::appendData(const ThreadSafeDataBuffer& data, long long offset, long long length) { m_items.append(BlobDataItem(data, offset, length)); } -void BlobData::appendFile(const String& path) +void BlobData::appendFile(Ref<BlobDataFileReference>&& file) { - m_items.append(BlobDataItem(path)); + file->startTrackingModifications(); + m_items.append(BlobDataItem(WTFMove(file))); } -void BlobData::appendFile(const String& path, long long offset, long long length, double expectedModificationTime) +void BlobData::appendFile(BlobDataFileReference* file, long long offset, long long length) { - m_items.append(BlobDataItem(path, offset, length, expectedModificationTime)); -} - -void BlobData::appendBlob(const URL& url, long long offset, long long length) -{ - m_items.append(BlobDataItem(url, offset, length)); + m_items.append(BlobDataItem(file, offset, length)); } void BlobData::swapItems(BlobDataItemList& items) @@ -99,17 +88,4 @@ void BlobData::swapItems(BlobDataItemList& items) m_items.swap(items); } - -BlobDataHandle::BlobDataHandle(std::unique_ptr<BlobData> data, long long size) -{ - UNUSED_PARAM(size); - m_internalURL = BlobURL::createInternalURL(); - ThreadableBlobRegistry::registerBlobURL(m_internalURL, std::move(data)); -} - -BlobDataHandle::~BlobDataHandle() -{ - ThreadableBlobRegistry::unregisterBlobURL(m_internalURL); -} - } // namespace WebCore diff --git a/Source/WebCore/platform/network/BlobData.h b/Source/WebCore/platform/network/BlobData.h index 7b25c4bd5..932d5ab8b 100644 --- a/Source/WebCore/platform/network/BlobData.h +++ b/Source/WebCore/platform/network/BlobData.h @@ -31,175 +31,99 @@ #ifndef BlobData_h #define BlobData_h -#include "FileSystem.h" +#include "BlobDataFileReference.h" +#include "ThreadSafeDataBuffer.h" #include "URL.h" #include <wtf/Forward.h> -#include <wtf/PassOwnPtr.h> +#include <wtf/RefCounted.h> #include <wtf/ThreadSafeRefCounted.h> #include <wtf/text/WTFString.h> namespace WebCore { -class RawData : public ThreadSafeRefCounted<RawData> { +class BlobDataItem { public: - static PassRefPtr<RawData> create() - { - return adoptRef(new RawData()); - } - - void detachFromCurrentThread(); + WEBCORE_EXPORT static const long long toEndOfFile; - const char* data() const { return m_data.data(); } - size_t length() const { return m_data.size(); } - Vector<char>* mutableData() { return &m_data; } + enum class Type { + Data, + File + }; -private: - RawData(); + Type type() const { return m_type; } - Vector<char> m_data; -}; + // For Data type. + const ThreadSafeDataBuffer& data() const { return m_data; } -struct BlobDataItem { - static const long long toEndOfFile; + // For File type. + BlobDataFileReference* file() const { return m_file.get(); } - // Default constructor. - BlobDataItem() - : type(Data) - , offset(0) - , length(toEndOfFile) - , expectedModificationTime(invalidFileTime()) - { - } + long long offset() const { return m_offset; } + WEBCORE_EXPORT long long length() const; // Computes file length if it's not known yet. - // Constructor for String type (complete string). - explicit BlobDataItem(PassRefPtr<RawData> data) - : type(Data) - , data(data) - , offset(0) - , length(toEndOfFile) - , expectedModificationTime(invalidFileTime()) - { - } +private: + friend class BlobData; - // Constructor for File type (complete file). - explicit BlobDataItem(const String& path) - : type(File) - , path(path) - , offset(0) - , length(toEndOfFile) - , expectedModificationTime(invalidFileTime()) + explicit BlobDataItem(Ref<BlobDataFileReference>&& file) + : m_type(Type::File) + , m_file(WTFMove(file)) + , m_offset(0) + , m_length(toEndOfFile) { } - // 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) + BlobDataItem(ThreadSafeDataBuffer data, long long offset, long long length) + : m_type(Type::Data) + , m_data(data) + , m_offset(offset) + , m_length(length) { } - // Constructor for Blob type. - BlobDataItem(const URL& url, long long offset, long long length) - : type(Blob) - , url(url) - , offset(offset) - , length(length) - , expectedModificationTime(invalidFileTime()) + BlobDataItem(BlobDataFileReference* file, long long offset, long long length) + : m_type(Type::File) + , m_file(file) + , m_offset(offset) + , m_length(length) { } - // 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; + Type m_type; + ThreadSafeDataBuffer m_data; + RefPtr<BlobDataFileReference> m_file; - // For Blob or URL type. - URL 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(invalidFileTime()) - { - } + long long m_offset; + long long m_length; }; typedef Vector<BlobDataItem> BlobDataItemList; -class BlobData { - WTF_MAKE_FAST_ALLOCATED; +class BlobData : public ThreadSafeRefCounted<BlobData> { public: - BlobData() { } - - // Detaches from current thread so that it can be passed to another thread. - void detachFromCurrentThread(); + static Ref<BlobData> create(const String& contentType) + { + return adoptRef(*new BlobData(contentType)); + } const String& contentType() const { return m_contentType; } - void setContentType(const String&); - - 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 URL&, long long offset, long long length); + void appendData(const ThreadSafeDataBuffer&); + void appendFile(Ref<BlobDataFileReference>&&); private: friend class BlobRegistryImpl; - friend class BlobStorageData; + BlobData(const String& contentType); - // This is only exposed to BlobStorageData. - void appendData(const RawData&, long long offset, long long length); + void appendData(const ThreadSafeDataBuffer&, long long offset, long long length); + void appendFile(BlobDataFileReference*, long long offset, long long length); String m_contentType; - String m_contentDisposition; BlobDataItemList m_items; }; -// FIXME: This class is mostly place holder until I get farther along with -// https://bugs.webkit.org/show_bug.cgi?id=108733 and more specifically with landing -// https://codereview.chromium.org/11192017/. -class BlobDataHandle : public ThreadSafeRefCounted<BlobDataHandle> { -public: - static PassRefPtr<BlobDataHandle> create(std::unique_ptr<BlobData> data, long long size) - { - return adoptRef(new BlobDataHandle(std::move(data), size)); - } - - ~BlobDataHandle(); - -private: - BlobDataHandle(std::unique_ptr<BlobData>, long long size); - URL m_internalURL; -}; - } // namespace WebCore #endif // BlobData_h diff --git a/Source/WebCore/platform/network/BlobDataFileReference.cpp b/Source/WebCore/platform/network/BlobDataFileReference.cpp new file mode 100644 index 000000000..1f8ce0b63 --- /dev/null +++ b/Source/WebCore/platform/network/BlobDataFileReference.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2014 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 "BlobDataFileReference.h" + +#include "File.h" +#include "FileMetadata.h" + +namespace WebCore { + +BlobDataFileReference::BlobDataFileReference(const String& path) + : m_path(path) +#if ENABLE(FILE_REPLACEMENT) + , m_replacementShouldBeGenerated(false) +#endif + , m_size(0) + , m_expectedModificationTime(invalidFileTime()) +{ +} + +BlobDataFileReference::~BlobDataFileReference() +{ +#if ENABLE(FILE_REPLACEMENT) + if (!m_replacementPath.isNull()) + deleteFile(m_replacementPath); +#endif +} + +const String& BlobDataFileReference::path() +{ +#if ENABLE(FILE_REPLACEMENT) + if (m_replacementShouldBeGenerated) + generateReplacementFile(); + + if (!m_replacementPath.isNull()) + return m_replacementPath; +#endif + + return m_path; +} + +unsigned long long BlobDataFileReference::size() +{ +#if ENABLE(FILE_REPLACEMENT) + if (m_replacementShouldBeGenerated) + generateReplacementFile(); +#endif + + return m_size; +} + +double BlobDataFileReference::expectedModificationTime() +{ +#if ENABLE(FILE_REPLACEMENT) + // We do not currently track modifications for generated files, because we have a snapshot. + // Unfortunately, this is inconsistent with regular file handling - File objects should be invalidated when underlying files change. + if (m_replacementShouldBeGenerated || !m_replacementPath.isNull()) + return invalidFileTime(); +#endif + + return m_expectedModificationTime; +} + +void BlobDataFileReference::startTrackingModifications() +{ + // This is not done automatically by the constructor, because BlobDataFileReference is + // also used to pass paths around before registration. Only registered blobs need to pay + // the cost of tracking file modifications. + +#if ENABLE(FILE_REPLACEMENT) + m_replacementShouldBeGenerated = File::shouldReplaceFile(m_path); +#endif + + // FIXME: Some platforms provide better ways to listen for file system object changes, consider using these. + FileMetadata metadata; + if (!getFileMetadata(m_path, metadata)) + return; + + m_expectedModificationTime = metadata.modificationTime; + +#if ENABLE(FILE_REPLACEMENT) + if (m_replacementShouldBeGenerated) + return; +#endif + + m_size = metadata.length; +} + +void BlobDataFileReference::prepareForFileAccess() +{ +} + +void BlobDataFileReference::revokeFileAccess() +{ +} + +} diff --git a/Source/WebCore/platform/network/BlobDataFileReference.h b/Source/WebCore/platform/network/BlobDataFileReference.h new file mode 100644 index 000000000..6fae977ad --- /dev/null +++ b/Source/WebCore/platform/network/BlobDataFileReference.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 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 BlobDataFileReference_h +#define BlobDataFileReference_h + +#include "FileSystem.h" +#include <wtf/RefCounted.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class WEBCORE_EXPORT BlobDataFileReference : public RefCounted<BlobDataFileReference> { +public: + static Ref<BlobDataFileReference> create(const String& path) + { + return adoptRef(*new BlobDataFileReference(path)); + } + + virtual ~BlobDataFileReference(); + + void startTrackingModifications(); + + const String& path(); + unsigned long long size(); + double expectedModificationTime(); + + virtual void prepareForFileAccess(); + virtual void revokeFileAccess(); + +protected: + BlobDataFileReference(const String& path); + +private: +#if ENABLE(FILE_REPLACEMENT) + void generateReplacementFile(); +#endif + + String m_path; +#if ENABLE(FILE_REPLACEMENT) + String m_replacementPath; + bool m_replacementShouldBeGenerated; +#endif + unsigned long long m_size; + double m_expectedModificationTime; +}; + +} + +#endif // BlobDataFileReference_h diff --git a/Source/WebCore/platform/network/BlobPart.h b/Source/WebCore/platform/network/BlobPart.h new file mode 100644 index 000000000..628f939a3 --- /dev/null +++ b/Source/WebCore/platform/network/BlobPart.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2014 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. + */ + +#pragma once + +#include "URL.h" + +namespace WebCore { + +class BlobPart { +public: + enum Type { + Data, + Blob + }; + + BlobPart() + : m_type(Data) + { + } + + BlobPart(Vector<uint8_t>&& data) + : m_type(Data) + , m_data(WTFMove(data)) + { + } + + BlobPart(const URL& url) + : m_type(Blob) + , m_url(url) + { + } + + Type type() const { return m_type; } + + const Vector<uint8_t>& data() const + { + ASSERT(m_type == Data); + return m_data; + } + + Vector<uint8_t> moveData() + { + ASSERT(m_type == Data); + return WTFMove(m_data); + } + + const URL& url() const + { + ASSERT(m_type == Blob); + return m_url; + } + + void detachFromCurrentThread() + { + m_url = m_url.isolatedCopy(); + } + +private: + Type m_type; + Vector<uint8_t> m_data; + URL m_url; +}; + +} diff --git a/Source/WebCore/platform/network/BlobRegistry.cpp b/Source/WebCore/platform/network/BlobRegistry.cpp index 4a61df4c9..b1fafc689 100644 --- a/Source/WebCore/platform/network/BlobRegistry.cpp +++ b/Source/WebCore/platform/network/BlobRegistry.cpp @@ -26,25 +26,15 @@ #include "config.h" #include "BlobRegistry.h" -#if ENABLE(BLOB) - -#include "BlobRegistryImpl.h" -#include "LoaderStrategy.h" #include "PlatformStrategies.h" #include <wtf/MainThread.h> -#if PLATFORM(IOS) -#include "WebCoreThread.h" -#endif - namespace WebCore { BlobRegistry& blobRegistry() { ASSERT(isMainThread()); - - static BlobRegistry& instance = *platformStrategies()->loaderStrategy()->createBlobRegistry(); - return instance; + return *platformStrategies()->blobRegistry(); } BlobRegistry::~BlobRegistry() @@ -52,5 +42,3 @@ BlobRegistry::~BlobRegistry() } } - -#endif diff --git a/Source/WebCore/platform/network/BlobRegistry.h b/Source/WebCore/platform/network/BlobRegistry.h index 868c46c37..b1216758c 100644 --- a/Source/WebCore/platform/network/BlobRegistry.h +++ b/Source/WebCore/platform/network/BlobRegistry.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2013, 2014, 2016 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 @@ -28,31 +29,47 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BlobRegistry_h -#define BlobRegistry_h +#pragma once -#include <wtf/PassOwnPtr.h> +#include <functional> +#include <wtf/Forward.h> +#include <wtf/Function.h> +#include <wtf/Vector.h> namespace WebCore { -class BlobData; -class BlobStorageData; +class BlobDataFileReference; +class BlobPart; class BlobRegistry; class URL; -BlobRegistry& blobRegistry(); +WEBCORE_EXPORT BlobRegistry& blobRegistry(); // BlobRegistry is not thread-safe. It should only be called from main thread. -class BlobRegistry { +class WEBCORE_EXPORT BlobRegistry { public: + + // Registers a blob URL referring to the specified file. + virtual void registerFileBlobURL(const URL&, Ref<BlobDataFileReference>&&, const String& contentType) = 0; + // Registers a blob URL referring to the specified blob data. - virtual void registerBlobURL(const URL&, std::unique_ptr<BlobData>) = 0; + virtual void registerBlobURL(const URL&, Vector<BlobPart>&&, const String& contentType) = 0; // Registers a new blob URL referring to the blob data identified by the specified srcURL. virtual void registerBlobURL(const URL&, const URL& srcURL) = 0; + // Registers a new blob URL referring to the blob data identified by the specified srcURL or, if none found, referring to the file found at the given path. + virtual void registerBlobURLOptionallyFileBacked(const URL&, const URL& srcURL, RefPtr<BlobDataFileReference>&&, const String& contentType) = 0; + + // Negative start and end values select from the end. + virtual void registerBlobURLForSlice(const URL&, const URL& srcURL, long long start, long long end) = 0; + virtual void unregisterBlobURL(const URL&) = 0; + virtual unsigned long long blobSize(const URL&) = 0; + + virtual void writeBlobsToTemporaryFiles(const Vector<String>& blobURLs, WTF::Function<void (const Vector<String>& filePaths)>&& completionHandler) = 0; + virtual bool isBlobRegistryImpl() const { return false; } protected: @@ -60,5 +77,3 @@ protected: }; } // namespace WebCore - -#endif // BlobRegistry_h diff --git a/Source/WebCore/platform/network/BlobRegistryImpl.cpp b/Source/WebCore/platform/network/BlobRegistryImpl.cpp index 231af78aa..e143451fd 100644 --- a/Source/WebCore/platform/network/BlobRegistryImpl.cpp +++ b/Source/WebCore/platform/network/BlobRegistryImpl.cpp @@ -32,20 +32,20 @@ #include "config.h" #include "BlobRegistryImpl.h" -#if ENABLE(BLOB) - +#include "BlobData.h" +#include "BlobPart.h" #include "BlobResourceHandle.h" -#include "BlobStorageData.h" +#include "FileMetadata.h" +#include "FileSystem.h" #include "ResourceError.h" #include "ResourceHandle.h" #include "ResourceRequest.h" #include "ResourceResponse.h" +#include "ScopeGuard.h" #include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> - -#if PLATFORM(IOS) -#include "WebCoreThread.h" -#endif +#include <wtf/WorkQueue.h> namespace WebCore { @@ -53,14 +53,14 @@ BlobRegistryImpl::~BlobRegistryImpl() { } -static PassRefPtr<ResourceHandle> createResourceHandle(const ResourceRequest& request, ResourceHandleClient* client) +static Ref<ResourceHandle> createBlobResourceHandle(const ResourceRequest& request, ResourceHandleClient* client) { return static_cast<BlobRegistryImpl&>(blobRegistry()).createResourceHandle(request, client); } -static void loadResourceSynchronously(NetworkingContext*, const ResourceRequest& request, StoredCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) +static void loadBlobResourceSynchronously(NetworkingContext*, const ResourceRequest& request, StoredCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) { - BlobStorageData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(request.url()); + BlobData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(request.url()); BlobResourceHandle::loadResourceSynchronously(blobData, request, error, response, data); } @@ -68,68 +68,64 @@ static void registerBlobResourceHandleConstructor() { static bool didRegister = false; if (!didRegister) { - ResourceHandle::registerBuiltinConstructor("blob", createResourceHandle); - ResourceHandle::registerBuiltinSynchronousLoader("blob", loadResourceSynchronously); + ResourceHandle::registerBuiltinConstructor("blob", createBlobResourceHandle); + ResourceHandle::registerBuiltinSynchronousLoader("blob", loadBlobResourceSynchronously); didRegister = true; } } -PassRefPtr<ResourceHandle> BlobRegistryImpl::createResourceHandle(const ResourceRequest& request, ResourceHandleClient* client) +Ref<ResourceHandle> BlobRegistryImpl::createResourceHandle(const ResourceRequest& request, ResourceHandleClient* client) { - RefPtr<BlobResourceHandle> handle = BlobResourceHandle::createAsync(getBlobDataFromURL(request.url()), request, client); - if (!handle) - return 0; - + auto handle = BlobResourceHandle::createAsync(getBlobDataFromURL(request.url()), request, client); handle->start(); - return handle.release(); + return WTFMove(handle); } -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) +void BlobRegistryImpl::appendStorageItems(BlobData* blobData, 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; + if (offset >= iter->length()) + offset -= iter->length(); else break; } } for (; iter != items.end() && length > 0; ++iter) { - long long currentLength = iter->length - offset; + 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); + if (iter->type() == BlobDataItem::Type::Data) + blobData->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); + ASSERT(iter->type() == BlobDataItem::Type::File); + blobData->appendFile(iter->file(), iter->offset() + offset, newLength); } length -= newLength; offset = 0; } + ASSERT(!length); +} + +void BlobRegistryImpl::registerFileBlobURL(const URL& url, Ref<BlobDataFileReference>&& file, const String& contentType) +{ + ASSERT(isMainThread()); + registerBlobResourceHandleConstructor(); + + auto blobData = BlobData::create(contentType); + blobData->appendFile(WTFMove(file)); + m_blobs.set(url.string(), WTFMove(blobData)); } -void BlobRegistryImpl::registerBlobURL(const URL& url, std::unique_ptr<BlobData> blobData) +void BlobRegistryImpl::registerBlobURL(const URL& url, Vector<BlobPart>&& blobParts, const String& contentType) { ASSERT(isMainThread()); registerBlobResourceHandleConstructor(); - RefPtr<BlobStorageData> blobStorageData = BlobStorageData::create(blobData->contentType(), blobData->contentDisposition()); + auto blobData = BlobData::create(contentType); // 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. @@ -137,35 +133,86 @@ void BlobRegistryImpl::registerBlobURL(const URL& url, std::unique_ptr<BlobData> // 3) The URL item is denoted by the URL, 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); + for (BlobPart& part : blobParts) { + switch (part.type()) { + case BlobPart::Data: { + auto movedData = part.moveData(); + auto data = ThreadSafeDataBuffer::adoptVector(movedData); + blobData->appendData(data); 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); + } + case BlobPart::Blob: { + if (auto blob = m_blobs.get(part.url().string())) { + for (const BlobDataItem& item : blob->items()) + blobData->m_items.append(item); + } break; } + } } - m_blobs.set(url.string(), blobStorageData); + m_blobs.set(url.string(), WTFMove(blobData)); } void BlobRegistryImpl::registerBlobURL(const URL& url, const URL& srcURL) { + registerBlobURLOptionallyFileBacked(url, srcURL, nullptr, { }); +} + +void BlobRegistryImpl::registerBlobURLOptionallyFileBacked(const URL& url, const URL& srcURL, RefPtr<BlobDataFileReference>&& file, const String& contentType) +{ ASSERT(isMainThread()); registerBlobResourceHandleConstructor(); - RefPtr<BlobStorageData> src = m_blobs.get(srcURL.string()); - ASSERT(src); - if (!src) + BlobData* src = getBlobDataFromURL(srcURL); + if (src) { + m_blobs.set(url.string(), src); return; + } + + if (!file || file->path().isEmpty()) + return; + + auto backingFile = BlobData::create(contentType); + backingFile->appendFile(file.releaseNonNull()); - m_blobs.set(url.string(), src); + m_blobs.set(url.string(), WTFMove(backingFile)); +} + +void BlobRegistryImpl::registerBlobURLForSlice(const URL& url, const URL& srcURL, long long start, long long end) +{ + ASSERT(isMainThread()); + BlobData* originalData = getBlobDataFromURL(srcURL); + if (!originalData) + return; + + unsigned long long originalSize = blobSize(srcURL); + + // Convert the negative value that is used to select from the end. + if (start < 0) + start = start + originalSize; + if (end < 0) + end = end + originalSize; + + // Clamp the range if it exceeds the size limit. + if (start < 0) + start = 0; + if (end < 0) + end = 0; + if (static_cast<unsigned long long>(start) >= originalSize) { + start = 0; + end = 0; + } else if (end < start) + end = start; + else if (static_cast<unsigned long long>(end) > originalSize) + end = originalSize; + + unsigned long long newLength = end - start; + auto newData = BlobData::create(originalData->contentType()); + + appendStorageItems(newData.ptr(), originalData->items(), start, newLength); + + m_blobs.set(url.string(), WTFMove(newData)); } void BlobRegistryImpl::unregisterBlobURL(const URL& url) @@ -174,12 +221,111 @@ void BlobRegistryImpl::unregisterBlobURL(const URL& url) m_blobs.remove(url.string()); } -BlobStorageData* BlobRegistryImpl::getBlobDataFromURL(const URL& url) const +BlobData* BlobRegistryImpl::getBlobDataFromURL(const URL& url) const { ASSERT(isMainThread()); return m_blobs.get(url.string()); } -} // namespace WebCore +unsigned long long BlobRegistryImpl::blobSize(const URL& url) +{ + ASSERT(isMainThread()); + BlobData* data = getBlobDataFromURL(url); + if (!data) + return 0; -#endif + unsigned long long result = 0; + for (const BlobDataItem& item : data->items()) + result += item.length(); + + return result; +} + +static WorkQueue& blobUtilityQueue() +{ + static NeverDestroyed<Ref<WorkQueue>> queue(WorkQueue::create("org.webkit.BlobUtility", WorkQueue::Type::Serial, WorkQueue::QOS::Background)); + return queue.get(); +} + +struct BlobForFileWriting { + String blobURL; + Vector<std::pair<String, ThreadSafeDataBuffer>> filePathsOrDataBuffers; +}; + +void BlobRegistryImpl::writeBlobsToTemporaryFiles(const Vector<String>& blobURLs, Function<void (const Vector<String>& filePaths)>&& completionHandler) +{ + Vector<BlobForFileWriting> blobsForWriting; + for (auto& url : blobURLs) { + blobsForWriting.append({ }); + blobsForWriting.last().blobURL = url.isolatedCopy(); + + auto* blobData = getBlobDataFromURL({ ParsedURLString, url }); + if (!blobData) { + Vector<String> filePaths; + completionHandler(filePaths); + return; + } + + for (auto& item : blobData->items()) { + switch (item.type()) { + case BlobDataItem::Type::Data: + blobsForWriting.last().filePathsOrDataBuffers.append({ { }, item.data() }); + break; + case BlobDataItem::Type::File: + blobsForWriting.last().filePathsOrDataBuffers.append({ item.file()->path().isolatedCopy(), { } }); + break; + default: + ASSERT_NOT_REACHED(); + } + } + } + + blobUtilityQueue().dispatch([blobsForWriting = WTFMove(blobsForWriting), completionHandler = WTFMove(completionHandler)]() mutable { + Vector<String> filePaths; + + auto performWriting = [blobsForWriting = WTFMove(blobsForWriting), &filePaths]() { + for (auto& blob : blobsForWriting) { + PlatformFileHandle file; + String tempFilePath = openTemporaryFile(ASCIILiteral("Blob"), file); + + ScopeGuard fileCloser([file]() mutable { + closeFile(file); + }); + + if (tempFilePath.isEmpty() || !isHandleValid(file)) { + LOG_ERROR("Failed to open temporary file for writing a Blob to IndexedDB"); + return false; + } + + for (auto& part : blob.filePathsOrDataBuffers) { + if (part.second.data()) { + int length = part.second.data()->size(); + if (writeToFile(file, reinterpret_cast<const char*>(part.second.data()->data()), length) != length) { + LOG_ERROR("Failed writing a Blob to temporary file for storage in IndexedDB"); + return false; + } + } else { + ASSERT(!part.first.isEmpty()); + if (!appendFileContentsToFileHandle(part.first, file)) { + LOG_ERROR("Failed copying File contents to a Blob temporary file for storage in IndexedDB (%s to %s)", part.first.utf8().data(), tempFilePath.utf8().data()); + return false; + } + } + } + + filePaths.append(tempFilePath.isolatedCopy()); + } + + return true; + }; + + if (!performWriting()) + filePaths.clear(); + + callOnMainThread([completionHandler = WTFMove(completionHandler), filePaths = WTFMove(filePaths)]() { + completionHandler(filePaths); + }); + }); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/BlobRegistryImpl.h b/Source/WebCore/platform/network/BlobRegistryImpl.h index 5e49f2088..09312fa1d 100644 --- a/Source/WebCore/platform/network/BlobRegistryImpl.h +++ b/Source/WebCore/platform/network/BlobRegistryImpl.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2013, 2014, 2016 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 @@ -28,11 +29,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BlobRegistryImpl_h -#define BlobRegistryImpl_h +#pragma once +#include "BlobData.h" #include "BlobRegistry.h" -#include "BlobStorageData.h" +#include "URLHash.h" #include <wtf/HashMap.h> #include <wtf/text/StringHash.h> #include <wtf/text/WTFString.h> @@ -45,27 +46,31 @@ class ResourceHandleClient; class ResourceRequest; // BlobRegistryImpl is not thread-safe. It should only be called from main thread. -class BlobRegistryImpl : public BlobRegistry { +class WEBCORE_EXPORT BlobRegistryImpl final : public BlobRegistry { WTF_MAKE_FAST_ALLOCATED; public: virtual ~BlobRegistryImpl(); - BlobStorageData* getBlobDataFromURL(const URL&) const; + BlobData* getBlobDataFromURL(const URL&) const; - PassRefPtr<ResourceHandle> createResourceHandle(const ResourceRequest&, ResourceHandleClient*); + Ref<ResourceHandle> createResourceHandle(const ResourceRequest&, ResourceHandleClient*); private: - void appendStorageItems(BlobStorageData*, const BlobDataItemList&); - void appendStorageItems(BlobStorageData*, const BlobDataItemList&, long long offset, long long length); + void appendStorageItems(BlobData*, const BlobDataItemList&, long long offset, long long length); - virtual void registerBlobURL(const URL&, std::unique_ptr<BlobData>) override; - virtual void registerBlobURL(const URL&, const URL& srcURL) override; - virtual void unregisterBlobURL(const URL&) override; - virtual bool isBlobRegistryImpl() const override { return true; } + void registerFileBlobURL(const URL&, Ref<BlobDataFileReference>&&, const String& contentType) override; + void registerBlobURL(const URL&, Vector<BlobPart>&&, const String& contentType) override; + void registerBlobURL(const URL&, const URL& srcURL) override; + void registerBlobURLOptionallyFileBacked(const URL&, const URL& srcURL, RefPtr<BlobDataFileReference>&&, const String& contentType) override; + void registerBlobURLForSlice(const URL&, const URL& srcURL, long long start, long long end) override; + void unregisterBlobURL(const URL&) override; + bool isBlobRegistryImpl() const override { return true; } - HashMap<String, RefPtr<BlobStorageData>> m_blobs; + unsigned long long blobSize(const URL&) override; + + void writeBlobsToTemporaryFiles(const Vector<String>& blobURLs, Function<void (const Vector<String>& filePaths)>&& completionHandler) override; + + HashMap<String, RefPtr<BlobData>> m_blobs; }; } // namespace WebCore - -#endif // BlobRegistryImpl_h diff --git a/Source/WebCore/platform/network/BlobResourceHandle.cpp b/Source/WebCore/platform/network/BlobResourceHandle.cpp index 670d14bb3..e06fa6561 100644 --- a/Source/WebCore/platform/network/BlobResourceHandle.cpp +++ b/Source/WebCore/platform/network/BlobResourceHandle.cpp @@ -30,15 +30,15 @@ #include "config.h" -#if ENABLE(BLOB) - #include "BlobResourceHandle.h" #include "AsyncFileStream.h" -#include "BlobStorageData.h" +#include "BlobData.h" #include "FileStream.h" #include "FileSystem.h" +#include "HTTPHeaderNames.h" #include "HTTPParsers.h" +#include "ParsedContentRange.h" #include "URL.h" #include "ResourceError.h" #include "ResourceHandleClient.h" @@ -51,29 +51,19 @@ namespace WebCore { static const unsigned bufferSize = 512 * 1024; -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 char* const webKitBlobResourceDomain = "WebKitBlobResource"; -enum { - notFoundError = 1, - securityError = 2, - rangeError = 3, - notReadableError = 4, - methodNotAllowed = 5 -}; /////////////////////////////////////////////////////////////////////////////// // BlobResourceSynchronousLoader @@ -84,10 +74,8 @@ class BlobResourceSynchronousLoader : public ResourceHandleClient { public: BlobResourceSynchronousLoader(ResourceError&, ResourceResponse&, Vector<char>&); - virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&) override; - virtual void didReceiveData(ResourceHandle*, const char*, unsigned, int /*encodedDataLength*/) override; - virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/) override; - virtual void didFail(ResourceHandle*, const ResourceError&) override; + void didReceiveResponse(ResourceHandle*, ResourceResponse&&) override; + void didFail(ResourceHandle*, const ResourceError&) override; private: ResourceError& m_error; @@ -102,11 +90,11 @@ BlobResourceSynchronousLoader::BlobResourceSynchronousLoader(ResourceError& erro { } -void BlobResourceSynchronousLoader::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) +void BlobResourceSynchronousLoader::didReceiveResponse(ResourceHandle* handle, ResourceResponse&& response) { // We cannot handle the size that is more than maximum integer. if (response.expectedContentLength() > INT_MAX) { - m_error = ResourceError(webKitBlobResourceDomain, notReadableError, response.url(), "File is too large"); + m_error = ResourceError(webKitBlobResourceDomain, static_cast<int>(BlobResourceHandle::Error::NotReadableError), response.url(), "File is too large"); return; } @@ -117,14 +105,6 @@ void BlobResourceSynchronousLoader::didReceiveResponse(ResourceHandle* handle, c static_cast<BlobResourceHandle*>(handle)->readSync(m_data.data(), static_cast<int>(m_data.size())); } -void BlobResourceSynchronousLoader::didReceiveData(ResourceHandle*, const char*, unsigned, int) -{ -} - -void BlobResourceSynchronousLoader::didFinishLoading(ResourceHandle*, double) -{ -} - void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError& error) { m_error = error; @@ -135,19 +115,15 @@ void BlobResourceSynchronousLoader::didFail(ResourceHandle*, const ResourceError /////////////////////////////////////////////////////////////////////////////// // BlobResourceHandle -PassRefPtr<BlobResourceHandle> BlobResourceHandle::createAsync(BlobStorageData* blobData, const ResourceRequest& request, ResourceHandleClient* client) +Ref<BlobResourceHandle> BlobResourceHandle::createAsync(BlobData* blobData, const ResourceRequest& request, ResourceHandleClient* client) { - // FIXME: Should probably call didFail() instead of blocking the load without explanation. - if (!equalIgnoringCase(request.httpMethod(), "GET")) - return 0; - - return adoptRef(new BlobResourceHandle(blobData, request, client, true)); + return adoptRef(*new BlobResourceHandle(blobData, request, client, true)); } -void BlobResourceHandle::loadResourceSynchronously(BlobStorageData* blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) +void BlobResourceHandle::loadResourceSynchronously(BlobData* blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) { - if (!equalIgnoringCase(request.httpMethod(), "GET")) { - error = ResourceError(webKitBlobResourceDomain, methodNotAllowed, response.url(), "Request method must be GET"); + if (!equalLettersIgnoringASCIICase(request.httpMethod(), "get")) { + error = ResourceError(webKitBlobResourceDomain, static_cast<int>(Error::MethodNotAllowed), response.url(), "Request method must be GET"); return; } @@ -156,89 +132,76 @@ void BlobResourceHandle::loadResourceSynchronously(BlobStorageData* blobData, co handle->start(); } -BlobResourceHandle::BlobResourceHandle(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async) - : ResourceHandle(0, request, client, false, false) +BlobResourceHandle::BlobResourceHandle(BlobData* blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async) + : ResourceHandle(nullptr, 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) - m_asyncStream = AsyncFileStream::create(this); + m_asyncStream = std::make_unique<AsyncFileStream>(*this); else - m_stream = FileStream::create(); + m_stream = std::make_unique<FileStream>(); } 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_asyncStream = nullptr; + m_fileOpened = false; m_aborted = true; ResourceHandle::cancel(); } -void delayedStartBlobResourceHandle(void* context) +void BlobResourceHandle::continueDidReceiveResponse() { - RefPtr<BlobResourceHandle> handle = adoptRef(static_cast<BlobResourceHandle*>(context)); - handle->doStart(); + ASSERT(m_async); + ASSERT(usesAsyncCallbacks()); + + m_buffer.resize(bufferSize); + readAsync(); } void BlobResourceHandle::start() { - if (m_async) { - // Keep BlobResourceHandle alive until delayedStartBlobResourceHandle runs. - ref(); - - // Finish this async call quickly and return. - callOnMainThread(delayedStartBlobResourceHandle, this); + if (!m_async) { + doStart(); return; } - doStart(); + // Finish this async call quickly and return. + callOnMainThread([protectedThis = makeRef(*this)]() mutable { + protectedThis->doStart(); + }); } void BlobResourceHandle::doStart() { + ASSERT(isMainThread()); + // Do not continue if the request is aborted or an error occurs. - if (m_aborted || m_errorCode) + if (erroredOrAborted()) return; + if (!equalLettersIgnoringASCIICase(firstRequest().httpMethod(), "get")) { + notifyFail(Error::MethodNotAllowed); + return; + } + // If the blob data is not found, fail now. if (!m_blobData) { - m_errorCode = notFoundError; - notifyResponse(); + notifyFail(Error::NotFoundError); return; } // Parse the "Range" header we care about. - String range = firstRequest().httpHeaderField("Range"); + String range = firstRequest().httpHeaderField(HTTPHeaderName::Range); if (!range.isEmpty() && !parseRange(range, m_rangeOffset, m_rangeEnd, m_rangeSuffixLength)) { - m_errorCode = rangeError; + m_errorCode = Error::RangeError; notifyResponse(); return; } @@ -246,8 +209,8 @@ void BlobResourceHandle::doStart() if (m_async) getSizeForNext(); else { - Ref<BlobResourceHandle> protect(*this); // getSizeForNext calls the client - for (size_t i = 0; i < m_blobData->items().size() && !m_aborted && !m_errorCode; ++i) + Ref<BlobResourceHandle> protectedThis(*this); // getSizeForNext calls the client + for (size_t i = 0; i < m_blobData->items().size() && !erroredOrAborted(); ++i) getSizeForNext(); notifyResponse(); } @@ -255,30 +218,35 @@ void BlobResourceHandle::doStart() void BlobResourceHandle::getSizeForNext() { + ASSERT(isMainThread()); + // 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) { - Ref<BlobResourceHandle> protect(*this); + Ref<BlobResourceHandle> protectedThis(*this); notifyResponse(); - m_buffer.resize(bufferSize); - readAsync(); + if (!usesAsyncCallbacks()) { + m_buffer.resize(bufferSize); + readAsync(); + } } return; } const BlobDataItem& item = m_blobData->items().at(m_sizeItemCount); - switch (item.type) { - case BlobDataItem::Data: - didGetSize(item.length); + switch (item.type()) { + case BlobDataItem::Type::Data: + didGetSize(item.length()); break; - case BlobDataItem::File: + case BlobDataItem::Type::File: + // Files know their sizes, but asking the stream to verify that the file wasn't modified. if (m_async) - m_asyncStream->getSize(item.path, item.expectedModificationTime); + m_asyncStream->getSize(item.file()->path(), item.file()->expectedModificationTime()); else - didGetSize(m_stream->getSize(item.path, item.expectedModificationTime)); + didGetSize(m_stream->getSize(item.file()->path(), item.file()->expectedModificationTime())); break; default: ASSERT_NOT_REACHED(); @@ -287,26 +255,27 @@ void BlobResourceHandle::getSizeForNext() void BlobResourceHandle::didGetSize(long long size) { + ASSERT(isMainThread()); + // Do not continue if the request is aborted or an error occurs. - if (m_aborted || m_errorCode) + if (erroredOrAborted()) return; // If the size is -1, it means the file has been moved or changed. Fail now. if (size == -1) { - m_errorCode = notFoundError; - notifyResponse(); + notifyFail(Error::NotFoundError); 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; + size = item.length(); // Cache the size. m_itemLengthList.append(size); // Count the size. + m_totalSize += size; m_totalRemainingSize += size; m_sizeItemCount++; @@ -316,14 +285,16 @@ void BlobResourceHandle::didGetSize(long long size) void BlobResourceHandle::seek() { + ASSERT(isMainThread()); + // Convert from the suffix length to the range. - if (m_rangeSuffixLength != positionNotSpecified) { + if (m_rangeSuffixLength != kPositionNotSpecified) { 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) + if (m_rangeOffset == kPositionNotSpecified) return; // Skip the initial items that are not in the range. @@ -335,7 +306,7 @@ void BlobResourceHandle::seek() m_currentItemReadSize = offset; // Adjust the total remaining size in order not to go beyond the range. - if (m_rangeEnd != positionNotSpecified) { + if (m_rangeEnd != kPositionNotSpecified) { long long rangeSize = m_rangeEnd - m_rangeOffset + 1; if (m_totalRemainingSize > rangeSize) m_totalRemainingSize = rangeSize; @@ -345,25 +316,27 @@ void BlobResourceHandle::seek() int BlobResourceHandle::readSync(char* buf, int length) { + ASSERT(isMainThread()); + ASSERT(!m_async); - Ref<BlobResourceHandle> protect(*this); + Ref<BlobResourceHandle> protectedThis(*this); 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) + if (erroredOrAborted()) 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) + if (item.type() == BlobDataItem::Type::Data) bytesRead = readDataSync(item, buf + offset, remaining); - else if (item.type == BlobDataItem::File) + else if (item.type() == BlobDataItem::Type::File) bytesRead = readFileSync(item, buf + offset, remaining); else ASSERT_NOT_REACHED(); @@ -375,7 +348,7 @@ int BlobResourceHandle::readSync(char* buf, int length) } int result; - if (m_aborted || m_errorCode) + if (erroredOrAborted()) result = -1; else result = length - remaining; @@ -391,17 +364,19 @@ int BlobResourceHandle::readSync(char* buf, int length) int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int length) { + ASSERT(isMainThread()); + ASSERT(!m_async); - long long remaining = item.length - m_currentItemReadSize; + 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); + memcpy(buf, item.data().data() + item.offset() + m_currentItemReadSize, bytesToRead); m_totalRemainingSize -= bytesToRead; m_currentItemReadSize += bytesToRead; - if (m_currentItemReadSize == item.length) { + if (m_currentItemReadSize == item.length()) { m_readItemCount++; m_currentItemReadSize = 0; } @@ -411,16 +386,18 @@ int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int le int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int length) { + ASSERT(isMainThread()); + 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); + bool success = m_stream->openForRead(item.file()->path(), item.offset() + m_currentItemReadSize, bytesToRead); m_currentItemReadSize = 0; if (!success) { - m_errorCode = notReadableError; + m_errorCode = Error::NotReadableError; return 0; } @@ -429,7 +406,7 @@ int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int le int bytesRead = m_stream->read(buf, length); if (bytesRead < 0) { - m_errorCode = notReadableError; + m_errorCode = Error::NotReadableError; return 0; } if (!bytesRead) { @@ -444,10 +421,11 @@ int BlobResourceHandle::readFileSync(const BlobDataItem& item, char* buf, int le void BlobResourceHandle::readAsync() { + ASSERT(isMainThread()); ASSERT(m_async); // Do not continue if the request is aborted or an error occurs. - if (m_aborted || m_errorCode) + if (erroredOrAborted()) return; // If there is no more remaining data to read, we are done. @@ -457,9 +435,9 @@ void BlobResourceHandle::readAsync() } const BlobDataItem& item = m_blobData->items().at(m_readItemCount); - if (item.type == BlobDataItem::Data) + if (item.type() == BlobDataItem::Type::Data) readDataAsync(item); - else if (item.type == BlobDataItem::File) + else if (item.type() == BlobDataItem::Type::File) readFileAsync(item); else ASSERT_NOT_REACHED(); @@ -467,18 +445,22 @@ void BlobResourceHandle::readAsync() void BlobResourceHandle::readDataAsync(const BlobDataItem& item) { + ASSERT(isMainThread()); ASSERT(m_async); - Ref<BlobResourceHandle> protect(*this); + ASSERT(item.data().data()); + + Ref<BlobResourceHandle> protectedThis(*this); - long long bytesToRead = item.length - m_currentItemReadSize; + long long bytesToRead = item.length() - m_currentItemReadSize; if (bytesToRead > m_totalRemainingSize) bytesToRead = m_totalRemainingSize; - consumeData(item.data->data() + item.offset + m_currentItemReadSize, static_cast<int>(bytesToRead)); + consumeData(reinterpret_cast<const char*>(item.data().data()->data()) + item.offset() + m_currentItemReadSize, static_cast<int>(bytesToRead)); m_currentItemReadSize = 0; } void BlobResourceHandle::readFileAsync(const BlobDataItem& item) { + ASSERT(isMainThread()); ASSERT(m_async); if (m_fileOpened) { @@ -489,7 +471,7 @@ void BlobResourceHandle::readFileAsync(const BlobDataItem& item) 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_asyncStream->openForRead(item.file()->path(), item.offset() + m_currentItemReadSize, bytesToRead); m_fileOpened = true; m_currentItemReadSize = 0; } @@ -499,7 +481,7 @@ void BlobResourceHandle::didOpen(bool success) ASSERT(m_async); if (!success) { - failed(notReadableError); + failed(Error::NotReadableError); return; } @@ -510,7 +492,7 @@ void BlobResourceHandle::didOpen(bool success) void BlobResourceHandle::didRead(int bytesRead) { if (bytesRead < 0) { - failed(notReadableError); + failed(Error::NotReadableError); return; } @@ -520,7 +502,7 @@ void BlobResourceHandle::didRead(int bytesRead) void BlobResourceHandle::consumeData(const char* data, int bytesRead) { ASSERT(m_async); - Ref<BlobResourceHandle> protect(*this); + Ref<BlobResourceHandle> protectedThis(*this); m_totalRemainingSize -= bytesRead; @@ -547,10 +529,10 @@ void BlobResourceHandle::consumeData(const char* data, int bytesRead) readAsync(); } -void BlobResourceHandle::failed(int errorCode) +void BlobResourceHandle::failed(Error errorCode) { ASSERT(m_async); - Ref<BlobResourceHandle> protect(*this); + Ref<BlobResourceHandle> protectedThis(*this); // Notify the client. notifyFail(errorCode); @@ -567,8 +549,8 @@ void BlobResourceHandle::notifyResponse() if (!client()) return; - if (m_errorCode) { - Ref<BlobResourceHandle> protect(*this); + if (m_errorCode != Error::NoError) { + Ref<BlobResourceHandle> protectedThis(*this); notifyResponseOnError(); notifyFinish(); } else @@ -577,38 +559,36 @@ void BlobResourceHandle::notifyResponse() void BlobResourceHandle::notifyResponseOnSuccess() { - bool isRangeRequest = m_rangeOffset != positionNotSpecified; - ResourceResponse response(firstRequest().url(), m_blobData->contentType(), m_totalRemainingSize, String(), String()); - response.setExpectedContentLength(m_totalRemainingSize); + ASSERT(isMainThread()); + + bool isRangeRequest = m_rangeOffset != kPositionNotSpecified; + ResourceResponse response(firstRequest().url(), m_blobData->contentType(), m_totalRemainingSize, String()); response.setHTTPStatusCode(isRangeRequest ? httpPartialContent : httpOK); response.setHTTPStatusText(isRangeRequest ? httpPartialContentText : httpOKText); - if (!m_blobData->contentDisposition().isEmpty()) - response.setHTTPHeaderField("Content-Disposition", m_blobData->contentDisposition()); - - // BlobResourceHandle cannot be used with downloading, and doesn't even wait for continueDidReceiveResponse. - // It's currently client's responsibility to know that didReceiveResponseAsync cannot be used to convert a - // load into a download or blobs. - if (client()->usesAsyncCallbacks()) - client()->didReceiveResponseAsync(this, response); - else - client()->didReceiveResponse(this, response); + + response.setHTTPHeaderField(HTTPHeaderName::ContentType, m_blobData->contentType()); + response.setHTTPHeaderField(HTTPHeaderName::ContentLength, String::number(m_totalRemainingSize)); + + if (isRangeRequest) + response.setHTTPHeaderField(HTTPHeaderName::ContentRange, ParsedContentRange(m_rangeOffset, m_rangeEnd, m_totalSize).headerValue()); + // FIXME: If a resource identified with a blob: URL is a File object, user agents must use that file's name attribute, + // as if the response had a Content-Disposition header with the filename parameter set to the File's name attribute. + // Notably, this will affect a name suggested in "File Save As". + + didReceiveResponse(WTFMove(response)); } void BlobResourceHandle::notifyResponseOnError() { - ASSERT(m_errorCode); + ASSERT(m_errorCode != Error::NoError); - ResourceResponse response(firstRequest().url(), "text/plain", 0, String(), String()); + ResourceResponse response(firstRequest().url(), "text/plain", 0, String()); switch (m_errorCode) { - case rangeError: + case Error::RangeError: response.setHTTPStatusCode(httpRequestedRangeNotSatisfiable); response.setHTTPStatusText(httpRequestedRangeNotSatisfiableText); break; - case notFoundError: - response.setHTTPStatusCode(httpNotFound); - response.setHTTPStatusText(httpNotFoundText); - break; - case securityError: + case Error::SecurityError: response.setHTTPStatusCode(httpNotAllowed); response.setHTTPStatusText(httpNotAllowedText); break; @@ -618,12 +598,7 @@ void BlobResourceHandle::notifyResponseOnError() break; } - // Note that we don't wait for continueDidReceiveResponse when using didReceiveResponseAsync. - // This is not formally correct, but the client has to be a no-op anyway, because blobs can't be downloaded. - if (client()->usesAsyncCallbacks()) - client()->didReceiveResponseAsync(this, response); - else - client()->didReceiveResponse(this, response); + didReceiveResponse(WTFMove(response)); } void BlobResourceHandle::notifyReceiveData(const char* data, int bytesRead) @@ -632,37 +607,36 @@ void BlobResourceHandle::notifyReceiveData(const char* data, int bytesRead) client()->didReceiveBuffer(this, SharedBuffer::create(data, bytesRead), bytesRead); } -void BlobResourceHandle::notifyFail(int errorCode) +void BlobResourceHandle::notifyFail(Error errorCode) { if (client()) - client()->didFail(this, ResourceError(webKitBlobResourceDomain, errorCode, firstRequest().url(), String())); + client()->didFail(this, ResourceError(webKitBlobResourceDomain, static_cast<int>(errorCode), firstRequest().url(), String())); } -static void doNotifyFinish(void* context) +static void doNotifyFinish(BlobResourceHandle& handle) { - BlobResourceHandle* handle = static_cast<BlobResourceHandle*>(context); - if (!handle->aborted() && handle->client()) - handle->client()->didFinishLoading(handle, 0); + if (handle.aborted()) + return; - // Balance the ref() in BlobResourceHandle::notfyFinish(). - handle->deref(); + if (!handle.client()) + return; + + handle.client()->didFinishLoading(&handle, 0); } void BlobResourceHandle::notifyFinish() { - // Balanced in doNotifyFinish(). - ref(); - - if (m_async) { - // Schedule to notify the client from a standalone function because the client might dispose the handle immediately from the callback function - // while we still have BlobResourceHandle calls in the stack. - callOnMainThread(doNotifyFinish, this); + if (!m_async) { + doNotifyFinish(*this); return; } - doNotifyFinish(this); + // Schedule to notify the client from a standalone function because the client might dispose the handle immediately from the callback function + // while we still have BlobResourceHandle calls in the stack. + callOnMainThread([protectedThis = makeRef(*this)]() mutable { + doNotifyFinish(protectedThis); + }); + } } // namespace WebCore - -#endif // ENABLE(BLOB) diff --git a/Source/WebCore/platform/network/BlobResourceHandle.h b/Source/WebCore/platform/network/BlobResourceHandle.h index 0ec849b00..f88217e34 100644 --- a/Source/WebCore/platform/network/BlobResourceHandle.h +++ b/Source/WebCore/platform/network/BlobResourceHandle.h @@ -28,56 +28,60 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BlobResourceHandle_h -#define BlobResourceHandle_h - -#if ENABLE(BLOB) +#pragma once #include "FileStreamClient.h" #include "ResourceHandle.h" -#include <wtf/PassRefPtr.h> #include <wtf/Vector.h> #include <wtf/text/WTFString.h> namespace WebCore { class AsyncFileStream; -class BlobStorageData; +class BlobData; class FileStream; class ResourceHandleClient; class ResourceRequest; -struct BlobDataItem; +class BlobDataItem; -class BlobResourceHandle : public FileStreamClient, public ResourceHandle { +class BlobResourceHandle final : public FileStreamClient, public ResourceHandle { public: - static PassRefPtr<BlobResourceHandle> createAsync(BlobStorageData*, const ResourceRequest&, ResourceHandleClient*); - - static void loadResourceSynchronously(BlobStorageData* blobData, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data); - - // FileStreamClient methods. - virtual void didGetSize(long long) override; - virtual void didOpen(bool) override; - virtual void didRead(int) override; + static Ref<BlobResourceHandle> createAsync(BlobData*, const ResourceRequest&, ResourceHandleClient*); - // ResourceHandle methods. - virtual void cancel() override; + static void loadResourceSynchronously(BlobData*, const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data); void start(); int readSync(char*, int); bool aborted() const { return m_aborted; } -private: - friend void delayedStartBlobResourceHandle(void*); + enum class Error { + NoError = 0, + NotFoundError = 1, + SecurityError = 2, + RangeError = 3, + NotReadableError = 4, + MethodNotAllowed = 5 + }; - BlobResourceHandle(PassRefPtr<BlobStorageData>, const ResourceRequest&, ResourceHandleClient*, bool async); +private: + BlobResourceHandle(BlobData*, const ResourceRequest&, ResourceHandleClient*, bool async); virtual ~BlobResourceHandle(); + // FileStreamClient methods. + void didGetSize(long long) override; + void didOpen(bool) override; + void didRead(int) override; + + // ResourceHandle methods. + void cancel() override; + void continueDidReceiveResponse() override; + void doStart(); void getSizeForNext(); void seek(); void consumeData(const char* data, int bytesRead); - void failed(int errorCode); + void failed(Error); void readAsync(); void readDataAsync(const BlobDataItem&); @@ -90,29 +94,30 @@ private: void notifyResponseOnSuccess(); void notifyResponseOnError(); void notifyReceiveData(const char*, int); - void notifyFail(int errorCode); + void notifyFail(Error); void notifyFinish(); - RefPtr<BlobStorageData> m_blobData; + bool erroredOrAborted() const { return m_aborted || m_errorCode != Error::NoError; } + + enum { kPositionNotSpecified = -1 }; + + RefPtr<BlobData> m_blobData; bool m_async; - RefPtr<AsyncFileStream> m_asyncStream; // For asynchronous loading. - RefPtr<FileStream> m_stream; // For synchronous loading. + std::unique_ptr<AsyncFileStream> m_asyncStream; // For asynchronous loading. + std::unique_ptr<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; + Error m_errorCode { Error::NoError }; + bool m_aborted { false }; + long long m_rangeOffset { kPositionNotSpecified }; + long long m_rangeEnd { kPositionNotSpecified }; + long long m_rangeSuffixLength { kPositionNotSpecified }; + long long m_totalSize { 0 }; + long long m_totalRemainingSize { 0 }; + long long m_currentItemReadSize { 0 }; + unsigned m_sizeItemCount { 0 }; + unsigned m_readItemCount { 0 }; + bool m_fileOpened { false }; }; } // namespace WebCore - -#endif // ENABLE(BLOB) - -#endif // BlobResourceHandle_h diff --git a/Source/WebCore/platform/network/CacheValidation.cpp b/Source/WebCore/platform/network/CacheValidation.cpp new file mode 100644 index 000000000..bed20e3a4 --- /dev/null +++ b/Source/WebCore/platform/network/CacheValidation.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2014-2016 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 "CacheValidation.h" + +#include "CookiesStrategy.h" +#include "HTTPHeaderMap.h" +#include "NetworkStorageSession.h" +#include "PlatformCookieJar.h" +#include "PlatformStrategies.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include <wtf/CurrentTime.h> +#include <wtf/text/StringView.h> + +namespace WebCore { + +// These response headers are not copied from a revalidated response to the +// cached response headers. For compatibility, this list is based on Chromium's +// net/http/http_response_headers.cc. +const char* const headersToIgnoreAfterRevalidation[] = { + "allow", + "connection", + "etag", + "keep-alive", + "last-modified", + "proxy-authenticate", + "proxy-connection", + "trailer", + "transfer-encoding", + "upgrade", + "www-authenticate", + "x-frame-options", + "x-xss-protection", +}; + +// Some header prefixes mean "Don't copy this header from a 304 response.". +// Rather than listing all the relevant headers, we can consolidate them into +// this list, also grabbed from Chromium's net/http/http_response_headers.cc. +const char* const headerPrefixesToIgnoreAfterRevalidation[] = { + "content-", + "x-content-", + "x-webkit-" +}; + +static inline bool shouldUpdateHeaderAfterRevalidation(const String& header) +{ + for (auto& headerToIgnore : headersToIgnoreAfterRevalidation) { + if (equalIgnoringASCIICase(header, headerToIgnore)) + return false; + } + for (size_t i = 0; i < WTF_ARRAY_LENGTH(headerPrefixesToIgnoreAfterRevalidation); i++) { + if (header.startsWith(headerPrefixesToIgnoreAfterRevalidation[i], false)) + return false; + } + return true; +} + +void updateResponseHeadersAfterRevalidation(ResourceResponse& response, const ResourceResponse& validatingResponse) +{ + // Freshening stored response upon validation: + // http://tools.ietf.org/html/rfc7234#section-4.3.4 + for (const auto& header : validatingResponse.httpHeaderFields()) { + // Entity headers should not be sent by servers when generating a 304 + // response; misconfigured servers send them anyway. We shouldn't allow + // such headers to update the original request. We'll base this on the + // list defined by RFC2616 7.1, with a few additions for extension headers + // we care about. + if (!shouldUpdateHeaderAfterRevalidation(header.key)) + continue; + response.setHTTPHeaderField(header.key, header.value); + } +} + +std::chrono::microseconds computeCurrentAge(const ResourceResponse& response, std::chrono::system_clock::time_point responseTime) +{ + using namespace std::chrono; + + // Age calculation: + // http://tools.ietf.org/html/rfc7234#section-4.2.3 + // No compensation for latency as that is not terribly important in practice. + auto dateValue = response.date(); + auto apparentAge = dateValue ? std::max(0us, duration_cast<microseconds>(responseTime - *dateValue)) : 0us; + auto ageValue = response.age().value_or(0us); + auto correctedInitialAge = std::max(apparentAge, ageValue); + auto residentTime = duration_cast<microseconds>(system_clock::now() - responseTime); + return correctedInitialAge + residentTime; +} + +std::chrono::microseconds computeFreshnessLifetimeForHTTPFamily(const ResourceResponse& response, std::chrono::system_clock::time_point responseTime) +{ + using namespace std::chrono; + ASSERT(response.url().protocolIsInHTTPFamily()); + + // Freshness Lifetime: + // http://tools.ietf.org/html/rfc7234#section-4.2.1 + auto maxAge = response.cacheControlMaxAge(); + if (maxAge) + return *maxAge; + + auto date = response.date(); + auto effectiveDate = date.value_or(responseTime); + if (auto expires = response.expires()) + return duration_cast<microseconds>(*expires - effectiveDate); + + // Implicit lifetime. + switch (response.httpStatusCode()) { + case 301: // Moved Permanently + case 410: // Gone + // These are semantically permanent and so get long implicit lifetime. + return 365 * 24h; + default: + // Heuristic Freshness: + // http://tools.ietf.org/html/rfc7234#section-4.2.2 + if (auto lastModified = response.lastModified()) + return duration_cast<microseconds>((effectiveDate - *lastModified) * 0.1); + return 0us; + } +} + +void updateRedirectChainStatus(RedirectChainCacheStatus& redirectChainCacheStatus, const ResourceResponse& response) +{ + using namespace std::chrono; + + if (redirectChainCacheStatus.status == RedirectChainCacheStatus::NotCachedRedirection) + return; + if (response.cacheControlContainsNoStore() || response.cacheControlContainsNoCache() || response.cacheControlContainsMustRevalidate()) { + redirectChainCacheStatus.status = RedirectChainCacheStatus::NotCachedRedirection; + return; + } + + redirectChainCacheStatus.status = RedirectChainCacheStatus::CachedRedirection; + auto responseTimestamp = system_clock::now(); + // Store the nearest end of cache validity date + auto endOfValidity = responseTimestamp + computeFreshnessLifetimeForHTTPFamily(response, responseTimestamp) - computeCurrentAge(response, responseTimestamp); + redirectChainCacheStatus.endOfValidity = std::min(redirectChainCacheStatus.endOfValidity, endOfValidity); +} + +bool redirectChainAllowsReuse(RedirectChainCacheStatus redirectChainCacheStatus, ReuseExpiredRedirectionOrNot reuseExpiredRedirection) +{ + switch (redirectChainCacheStatus.status) { + case RedirectChainCacheStatus::NoRedirection: + return true; + case RedirectChainCacheStatus::NotCachedRedirection: + return false; + case RedirectChainCacheStatus::CachedRedirection: + return reuseExpiredRedirection || std::chrono::system_clock::now() <= redirectChainCacheStatus.endOfValidity; + } + ASSERT_NOT_REACHED(); + return false; +} + +inline bool isCacheHeaderSeparator(UChar c) +{ + // http://tools.ietf.org/html/rfc7230#section-3.2.6 + 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; + } +} + +inline bool isControlCharacterOrSpace(UChar character) +{ + return character <= ' ' || character == 127; +} + +inline StringView trimToNextSeparator(StringView string) +{ + return string.substring(0, string.find(isCacheHeaderSeparator)); +} + +static Vector<std::pair<String, String>> parseCacheHeader(const String& header) +{ + Vector<std::pair<String, String>> result; + + String safeHeaderString = header.removeCharacters(isControlCharacterOrSpace); + StringView safeHeader = safeHeaderString; + unsigned max = safeHeader.length(); + unsigned pos = 0; + while (pos < max) { + size_t nextCommaPosition = safeHeader.find(',', pos); + size_t nextEqualSignPosition = safeHeader.find('=', pos); + if (nextEqualSignPosition == notFound && nextCommaPosition == notFound) { + // Add last directive to map with empty string as value + result.append({ trimToNextSeparator(safeHeader.substring(pos, max - pos)).toString(), emptyString() }); + return result; + } + if (nextCommaPosition != notFound && (nextCommaPosition < nextEqualSignPosition || nextEqualSignPosition == notFound)) { + // Add directive to map with empty string as value + result.append({ trimToNextSeparator(safeHeader.substring(pos, nextCommaPosition - pos)).toString(), emptyString() }); + pos += nextCommaPosition - pos + 1; + continue; + } + // Get directive name, parse right hand side of equal sign, then add to map + String directive = trimToNextSeparator(safeHeader.substring(pos, nextEqualSignPosition - pos)).toString(); + pos += nextEqualSignPosition - pos + 1; + + StringView value = safeHeader.substring(pos, max - pos); + if (value[0] == '"') { + // The value is a quoted string + size_t nextDoubleQuotePosition = value.find('"', 1); + if (nextDoubleQuotePosition == notFound) { + // Parse error; just use the rest as the value + result.append({ directive, trimToNextSeparator(value.substring(1)).toString() }); + return result; + } + // Store the value as a quoted string without quotes + result.append({ directive, value.substring(1, nextDoubleQuotePosition - 1).toString() }); + pos += (safeHeader.find('"', pos) - pos) + nextDoubleQuotePosition + 1; + // Move past next comma, if there is one + size_t nextCommaPosition2 = safeHeader.find(',', pos); + if (nextCommaPosition2 == notFound) + return result; // Parse error if there is anything left with no comma + pos += nextCommaPosition2 - pos + 1; + continue; + } + // The value is a token until the next comma + size_t nextCommaPosition2 = value.find(','); + if (nextCommaPosition2 == notFound) { + // The rest is the value; no change to value needed + result.append({ directive, trimToNextSeparator(value).toString() }); + return result; + } + // The value is delimited by the next comma + result.append({ directive, trimToNextSeparator(value.substring(0, nextCommaPosition2)).toString() }); + pos += (safeHeader.find(',', pos) - pos) + 1; + } + return result; +} + +CacheControlDirectives parseCacheControlDirectives(const HTTPHeaderMap& headers) +{ + using namespace std::chrono; + + CacheControlDirectives result; + + String cacheControlValue = headers.get(HTTPHeaderName::CacheControl); + if (!cacheControlValue.isEmpty()) { + auto directives = parseCacheHeader(cacheControlValue); + + size_t directivesSize = directives.size(); + for (size_t i = 0; i < directivesSize; ++i) { + // A no-cache directive with a value is only meaningful for proxy caches. + // It should be ignored by a browser level cache. + // http://tools.ietf.org/html/rfc7234#section-5.2.2.2 + if (equalLettersIgnoringASCIICase(directives[i].first, "no-cache") && directives[i].second.isEmpty()) + result.noCache = true; + else if (equalLettersIgnoringASCIICase(directives[i].first, "no-store")) + result.noStore = true; + else if (equalLettersIgnoringASCIICase(directives[i].first, "must-revalidate")) + result.mustRevalidate = true; + else if (equalLettersIgnoringASCIICase(directives[i].first, "max-age")) { + if (result.maxAge) { + // First max-age directive wins if there are multiple ones. + continue; + } + bool ok; + double maxAge = directives[i].second.toDouble(&ok); + if (ok) + result.maxAge = duration_cast<microseconds>(duration<double>(maxAge)); + } else if (equalLettersIgnoringASCIICase(directives[i].first, "max-stale")) { + // https://tools.ietf.org/html/rfc7234#section-5.2.1.2 + if (result.maxStale) { + // First max-stale directive wins if there are multiple ones. + continue; + } + if (directives[i].second.isEmpty()) { + // if no value is assigned to max-stale, then the client is willing to accept a stale response of any age. + result.maxStale = microseconds::max(); + continue; + } + bool ok; + double maxStale = directives[i].second.toDouble(&ok); + if (ok) + result.maxStale = duration_cast<microseconds>(duration<double>(maxStale)); + } else if (equalLettersIgnoringASCIICase(directives[i].first, "immutable")) + result.immutable = true; + } + } + + if (!result.noCache) { + // Handle Pragma: no-cache + // This is deprecated and equivalent to Cache-control: no-cache + // Don't bother tokenizing the value, it is not important + String pragmaValue = headers.get(HTTPHeaderName::Pragma); + + result.noCache = pragmaValue.contains("no-cache", false); + } + + return result; +} + +static String headerValueForVary(const ResourceRequest& request, const String& headerName, SessionID sessionID) +{ + // Explicit handling for cookies is needed because they are added magically by the networking layer. + // FIXME: The value might have changed between making the request and retrieving the cookie here. + // We could fetch the cookie when making the request but that seems overkill as the case is very rare and it + // is a blocking operation. This should be sufficient to cover reasonable cases. + if (headerName == httpHeaderNameString(HTTPHeaderName::Cookie)) { + auto* cookieStrategy = platformStrategies() ? platformStrategies()->cookiesStrategy() : nullptr; + if (!cookieStrategy) { + ASSERT(sessionID == SessionID::defaultSessionID()); + return cookieRequestHeaderFieldValue(NetworkStorageSession::defaultStorageSession(), request.firstPartyForCookies(), request.url()); + } + return cookieStrategy->cookieRequestHeaderFieldValue(sessionID, request.firstPartyForCookies(), request.url()); + } + return request.httpHeaderField(headerName); +} + +Vector<std::pair<String, String>> collectVaryingRequestHeaders(const WebCore::ResourceRequest& request, const WebCore::ResourceResponse& response, SessionID sessionID) +{ + String varyValue = response.httpHeaderField(WebCore::HTTPHeaderName::Vary); + if (varyValue.isEmpty()) + return { }; + Vector<String> varyingHeaderNames; + varyValue.split(',', /*allowEmptyEntries*/ false, varyingHeaderNames); + Vector<std::pair<String, String>> varyingRequestHeaders; + varyingRequestHeaders.reserveCapacity(varyingHeaderNames.size()); + for (auto& varyHeaderName : varyingHeaderNames) { + String headerName = varyHeaderName.stripWhiteSpace(); + String headerValue = headerValueForVary(request, headerName, sessionID); + varyingRequestHeaders.append(std::make_pair(headerName, headerValue)); + } + return varyingRequestHeaders; +} + +bool verifyVaryingRequestHeaders(const Vector<std::pair<String, String>>& varyingRequestHeaders, const WebCore::ResourceRequest& request, SessionID sessionID) +{ + for (auto& varyingRequestHeader : varyingRequestHeaders) { + // FIXME: Vary: * in response would ideally trigger a cache delete instead of a store. + if (varyingRequestHeader.first == "*") + return false; + String headerValue = headerValueForVary(request, varyingRequestHeader.first, sessionID); + if (headerValue != varyingRequestHeader.second) + return false; + } + return true; +} + +// http://tools.ietf.org/html/rfc7231#page-48 +bool isStatusCodeCacheableByDefault(int statusCode) +{ + switch (statusCode) { + case 200: // OK + case 203: // Non-Authoritative Information + case 204: // No Content + case 206: // Partial Content + case 300: // Multiple Choices + case 301: // Moved Permanently + case 404: // Not Found + case 405: // Method Not Allowed + case 410: // Gone + case 414: // URI Too Long + case 501: // Not Implemented + return true; + default: + return false; + } +} + +bool isStatusCodePotentiallyCacheable(int statusCode) +{ + switch (statusCode) { + case 201: // Created + case 202: // Accepted + case 205: // Reset Content + case 302: // Found + case 303: // See Other + case 307: // Temporary redirect + case 403: // Forbidden + case 406: // Not Acceptable + case 415: // Unsupported Media Type + return true; + default: + return false; + } +} + +} diff --git a/Source/WebCore/platform/network/CacheValidation.h b/Source/WebCore/platform/network/CacheValidation.h new file mode 100644 index 000000000..bd03c5137 --- /dev/null +++ b/Source/WebCore/platform/network/CacheValidation.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2014-2016 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 CacheValidation_h +#define CacheValidation_h + +#include "PlatformExportMacros.h" +#include "SessionID.h" +#include <wtf/Optional.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class HTTPHeaderMap; +class ResourceRequest; +class ResourceResponse; + +struct RedirectChainCacheStatus { + enum Status { + NoRedirection, + NotCachedRedirection, + CachedRedirection + }; + RedirectChainCacheStatus() + : status(NoRedirection) + , endOfValidity(std::chrono::system_clock::time_point::max()) + { } + Status status; + std::chrono::system_clock::time_point endOfValidity; +}; + +WEBCORE_EXPORT std::chrono::microseconds computeCurrentAge(const ResourceResponse&, std::chrono::system_clock::time_point responseTimestamp); +WEBCORE_EXPORT std::chrono::microseconds computeFreshnessLifetimeForHTTPFamily(const ResourceResponse&, std::chrono::system_clock::time_point responseTimestamp); +WEBCORE_EXPORT void updateResponseHeadersAfterRevalidation(ResourceResponse&, const ResourceResponse& validatingResponse); +WEBCORE_EXPORT void updateRedirectChainStatus(RedirectChainCacheStatus&, const ResourceResponse&); + +enum ReuseExpiredRedirectionOrNot { DoNotReuseExpiredRedirection, ReuseExpiredRedirection }; +WEBCORE_EXPORT bool redirectChainAllowsReuse(RedirectChainCacheStatus, ReuseExpiredRedirectionOrNot); + +struct CacheControlDirectives { + std::optional<std::chrono::microseconds> maxAge; + std::optional<std::chrono::microseconds> maxStale; + bool noCache { false }; + bool noStore { false }; + bool mustRevalidate { false }; + bool immutable { false }; +}; +WEBCORE_EXPORT CacheControlDirectives parseCacheControlDirectives(const HTTPHeaderMap&); + +WEBCORE_EXPORT Vector<std::pair<String, String>> collectVaryingRequestHeaders(const ResourceRequest&, const ResourceResponse&, SessionID = SessionID::defaultSessionID()); +WEBCORE_EXPORT bool verifyVaryingRequestHeaders(const Vector<std::pair<String, String>>& varyingRequestHeaders, const ResourceRequest&, SessionID = SessionID::defaultSessionID()); + +WEBCORE_EXPORT bool isStatusCodeCacheableByDefault(int statusCode); +WEBCORE_EXPORT bool isStatusCodePotentiallyCacheable(int statusCode); + +} + +#endif diff --git a/Source/WebCore/platform/network/CookieStorage.h b/Source/WebCore/platform/network/CookieStorage.h index 1c6947718..c5053cc90 100644 --- a/Source/WebCore/platform/network/CookieStorage.h +++ b/Source/WebCore/platform/network/CookieStorage.h @@ -23,17 +23,16 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CookieStorage_h -#define CookieStorage_h +#pragma once + +#include <functional> namespace WebCore { -// These are always observing the shared cookie storage, even when in private browsing mode. +class NetworkStorageSession; -typedef void(*CookieChangeCallbackPtr)(); -void startObservingCookieChanges(CookieChangeCallbackPtr); -void stopObservingCookieChanges(); +WEBCORE_EXPORT void startObservingCookieChanges(const NetworkStorageSession&, std::function<void ()>&&); +WEBCORE_EXPORT void stopObservingCookieChanges(const NetworkStorageSession&); } -#endif diff --git a/Source/WebCore/platform/network/Credential.cpp b/Source/WebCore/platform/network/Credential.cpp deleted file mode 100644 index 4b2440305..000000000 --- a/Source/WebCore/platform/network/Credential.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * 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 index 2166b9bfa..b0fb0dcd7 100644 --- a/Source/WebCore/platform/network/Credential.h +++ b/Source/WebCore/platform/network/Credential.h @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -22,69 +22,40 @@ * (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 <wtf/text/WTFString.h> +#include <wtf/Platform.h> -#define CERTIFICATE_CREDENTIALS_SUPPORTED (PLATFORM(IOS) || PLATFORM(MAC)) +#if PLATFORM(COCOA) +#include "CredentialCocoa.h" +#else -#if CERTIFICATE_CREDENTIALS_SUPPORTED -#include <Security/SecBase.h> -#include <wtf/RetainPtr.h> -#endif +#include "CredentialBase.h" namespace WebCore { -enum CredentialPersistence { - CredentialPersistenceNone, - CredentialPersistenceForSession, - CredentialPersistencePermanent -}; - -#if CERTIFICATE_CREDENTIALS_SUPPORTED -enum CredentialType { - CredentialTypePassword, - CredentialTypeClientCertificate +class Credential : public CredentialBase { +public: + Credential() + : CredentialBase() + { + } + + Credential(const String& user, const String& password, CredentialPersistence persistence) + : CredentialBase(user, password, persistence) + { + } + + Credential(const Credential& original, CredentialPersistence persistence) + : CredentialBase(original, persistence) + { + } }; -#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 +#endif // Credential_h diff --git a/Source/WebCore/platform/network/CredentialBase.cpp b/Source/WebCore/platform/network/CredentialBase.cpp new file mode 100644 index 000000000..435a99cf1 --- /dev/null +++ b/Source/WebCore/platform/network/CredentialBase.cpp @@ -0,0 +1,97 @@ +/* + * 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 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 "CredentialBase.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 +CredentialBase::CredentialBase() + : m_user(emptyString()) + , m_password(emptyString()) + , m_persistence(CredentialPersistenceNone) +{ +} + +// 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 +CredentialBase::CredentialBase(const String& user, const String& password, CredentialPersistence persistence) + : m_user(user.length() ? user : emptyString()) + , m_password(password.length() ? password : emptyString()) + , m_persistence(persistence) +{ +} + +CredentialBase::CredentialBase(const Credential& original, CredentialPersistence persistence) + : m_user(original.user()) + , m_password(original.password()) + , m_persistence(persistence) +{ +} + +bool CredentialBase::isEmpty() const +{ + return m_user.isEmpty() && m_password.isEmpty(); +} + +const String& CredentialBase::user() const +{ + return m_user; +} + +const String& CredentialBase::password() const +{ + return m_password; +} + +bool CredentialBase::hasPassword() const +{ + return !m_password.isEmpty(); +} + +CredentialPersistence CredentialBase::persistence() const +{ + return m_persistence; +} + +bool CredentialBase::compare(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 (a.user() != b.user()) + return false; + if (a.password() != b.password()) + return false; + + return Credential::platformCompare(a, b); +} + +} diff --git a/Source/WebCore/platform/network/CredentialBase.h b/Source/WebCore/platform/network/CredentialBase.h new file mode 100644 index 000000000..8fb49716d --- /dev/null +++ b/Source/WebCore/platform/network/CredentialBase.h @@ -0,0 +1,74 @@ +/* + * 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 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 CredentialBase_h +#define CredentialBase_h + +#include "PlatformExportMacros.h" +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class Credential; + +enum CredentialPersistence { + CredentialPersistenceNone, + CredentialPersistenceForSession, + CredentialPersistencePermanent +}; + +class CredentialBase { + +public: + bool isEmpty() const; + + WEBCORE_EXPORT const String& user() const; + WEBCORE_EXPORT const String& password() const; + WEBCORE_EXPORT bool hasPassword() const; + WEBCORE_EXPORT CredentialPersistence persistence() const; + + bool encodingRequiresPlatformData() const { return false; } + + WEBCORE_EXPORT static bool compare(const Credential&, const Credential&); + +protected: + WEBCORE_EXPORT CredentialBase(); + WEBCORE_EXPORT CredentialBase(const String& user, const String& password, CredentialPersistence); + CredentialBase(const Credential& original, CredentialPersistence); + + static bool platformCompare(const Credential&, const Credential&) { return true; } + +private: + String m_user; + String m_password; + CredentialPersistence m_persistence; +}; + +inline bool operator==(const Credential& a, const Credential& b) { return CredentialBase::compare(a, 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 index d86fd81c4..4ae98a66f 100644 --- a/Source/WebCore/platform/network/CredentialStorage.cpp +++ b/Source/WebCore/platform/network/CredentialStorage.cpp @@ -26,15 +26,8 @@ #include "config.h" #include "CredentialStorage.h" -#include "Credential.h" +#include "NetworkStorageSession.h" #include "URL.h" -#include "ProtectionSpaceHash.h" -#include <wtf/text/WTFString.h> -#include <wtf/text/StringHash.h> -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include <wtf/MainThread.h> -#include <wtf/StdLibExtras.h> #if PLATFORM(IOS) #include "WebCoreThread.h" @@ -42,35 +35,14 @@ namespace WebCore { -typedef HashMap<ProtectionSpace, Credential> ProtectionSpaceToCredentialMap; -static ProtectionSpaceToCredentialMap& protectionSpaceToCredentialMap() +CredentialStorage& CredentialStorage::defaultCredentialStorage() { - ASSERT(isMainThread()); - DEFINE_STATIC_LOCAL(ProtectionSpaceToCredentialMap, map, ()); - return map; -} - -static HashSet<String>& originsWithCredentials() -{ - ASSERT(isMainThread()); - DEFINE_STATIC_LOCAL(HashSet<String>, set, ()); - return set; -} - -typedef HashMap<String, ProtectionSpace> PathToDefaultProtectionSpaceMap; -static PathToDefaultProtectionSpaceMap& pathToDefaultProtectionSpaceMap() -{ - ASSERT(isMainThread()); - DEFINE_STATIC_LOCAL(PathToDefaultProtectionSpaceMap, map, ()); - return map; + return NetworkStorageSession::defaultStorageSession().credentialStorage(); } static String originStringFromURL(const URL& url) { - if (url.port()) - return url.protocol() + "://" + url.host() + ':' + String::number(url.port()) + '/'; - - return url.protocol() + "://" + url.host() + '/'; + return makeString(url.protocol(), "://", url.hostAndPort(), '/'); } static String protectionSpaceMapKeyFromURL(const URL& url) @@ -91,100 +63,90 @@ static String protectionSpaceMapKeyFromURL(const URL& url) return directoryURL; } -void CredentialStorage::set(const Credential& credential, const ProtectionSpace& protectionSpace, const URL& url) +void CredentialStorage::set(const String& partitionName, const Credential& credential, const ProtectionSpace& protectionSpace, const URL& url) { - ASSERT(protectionSpace.isProxy() || url.protocolIsInHTTPFamily()); - ASSERT(protectionSpace.isProxy() || url.isValid()); + ASSERT(protectionSpace.isProxy() || protectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeClientCertificateRequested || url.protocolIsInHTTPFamily()); + ASSERT(protectionSpace.isProxy() || protectionSpace.authenticationScheme() == ProtectionSpaceAuthenticationSchemeClientCertificateRequested || url.isValid()); - protectionSpaceToCredentialMap().set(protectionSpace, credential); + m_protectionSpaceToCredentialMap.set(std::make_pair(partitionName, protectionSpace), credential); #if PLATFORM(IOS) - saveToPersistentStorage(protectionSpace, credential); + if (protectionSpace.authenticationScheme() != ProtectionSpaceAuthenticationSchemeClientCertificateRequested) + saveToPersistentStorage(protectionSpace, credential); #endif - if (!protectionSpace.isProxy()) { - originsWithCredentials().add(originStringFromURL(url)); + if (!protectionSpace.isProxy() && protectionSpace.authenticationScheme() != ProtectionSpaceAuthenticationSchemeClientCertificateRequested) { + m_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); + m_pathToDefaultProtectionSpaceMap.set(protectionSpaceMapKeyFromURL(url), protectionSpace); } } } -Credential CredentialStorage::get(const ProtectionSpace& protectionSpace) +Credential CredentialStorage::get(const String& partitionName, const ProtectionSpace& protectionSpace) { - return protectionSpaceToCredentialMap().get(protectionSpace); + return m_protectionSpaceToCredentialMap.get(std::make_pair(partitionName, protectionSpace)); } -void CredentialStorage::remove(const ProtectionSpace& protectionSpace) +void CredentialStorage::remove(const String& partitionName, const ProtectionSpace& protectionSpace) { - protectionSpaceToCredentialMap().remove(protectionSpace); + m_protectionSpaceToCredentialMap.remove(std::make_pair(partitionName, protectionSpace)); } -static PathToDefaultProtectionSpaceMap::iterator findDefaultProtectionSpaceForURL(const URL& url) +HashMap<String, ProtectionSpace>::iterator CredentialStorage::findDefaultProtectionSpaceForURL(const URL& url) { ASSERT(url.protocolIsInHTTPFamily()); 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(); + if (!m_originsWithCredentials.contains(originStringFromURL(url))) + return m_pathToDefaultProtectionSpaceMap.end(); String directoryURL = protectionSpaceMapKeyFromURL(url); unsigned directoryURLPathStart = url.pathStart(); while (true) { - PathToDefaultProtectionSpaceMap::iterator iter = map.find(directoryURL); - if (iter != map.end()) + PathToDefaultProtectionSpaceMap::iterator iter = m_pathToDefaultProtectionSpaceMap.find(directoryURL); + if (iter != m_pathToDefaultProtectionSpaceMap.end()) return iter; if (directoryURL.length() == directoryURLPathStart + 1) // path is "/" already, cannot shorten it any more - return map.end(); + return m_pathToDefaultProtectionSpaceMap.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 URL& url) +bool CredentialStorage::set(const String& partitionName, const Credential& credential, const URL& url) { ASSERT(url.protocolIsInHTTPFamily()); ASSERT(url.isValid()); PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url); - if (iter == pathToDefaultProtectionSpaceMap().end()) + if (iter == m_pathToDefaultProtectionSpaceMap.end()) return false; - ASSERT(originsWithCredentials().contains(originStringFromURL(url))); - protectionSpaceToCredentialMap().set(iter->value, credential); + ASSERT(m_originsWithCredentials.contains(originStringFromURL(url))); + m_protectionSpaceToCredentialMap.set(std::make_pair(partitionName, iter->value), credential); return true; } -Credential CredentialStorage::get(const URL& url) +Credential CredentialStorage::get(const String& partitionName, const URL& url) { PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url); - if (iter == pathToDefaultProtectionSpaceMap().end()) + if (iter == m_pathToDefaultProtectionSpaceMap.end()) return Credential(); - return protectionSpaceToCredentialMap().get(iter->value); + return m_protectionSpaceToCredentialMap.get(std::make_pair(partitionName, iter->value)); } -#if PLATFORM(IOS) void CredentialStorage::clearCredentials() { - pathToDefaultProtectionSpaceMap().clear(); - originsWithCredentials().clear(); - protectionSpaceToCredentialMap().clear(); -} -#endif - -void CredentialStorage::setPrivateMode(bool mode) -{ - if (!mode) - protectionSpaceToCredentialMap().clear(); + m_protectionSpaceToCredentialMap.clear(); + m_originsWithCredentials.clear(); + m_pathToDefaultProtectionSpaceMap.clear(); } } // namespace WebCore diff --git a/Source/WebCore/platform/network/CredentialStorage.h b/Source/WebCore/platform/network/CredentialStorage.h index 3c8c74875..8280caeae 100644 --- a/Source/WebCore/platform/network/CredentialStorage.h +++ b/Source/WebCore/platform/network/CredentialStorage.h @@ -26,33 +26,49 @@ #ifndef CredentialStorage_h #define CredentialStorage_h +#include "Credential.h" +#include "ProtectionSpaceHash.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + namespace WebCore { -class Credential; class URL; class ProtectionSpace; class CredentialStorage { public: + WEBCORE_EXPORT static CredentialStorage& defaultCredentialStorage(); + // WebCore session credential storage. - static void set(const Credential&, const ProtectionSpace&, const URL&); - static Credential get(const ProtectionSpace&); - static void remove(const ProtectionSpace&); + WEBCORE_EXPORT void set(const String&, const Credential&, const ProtectionSpace&, const URL&); + WEBCORE_EXPORT Credential get(const String&, const ProtectionSpace&); + WEBCORE_EXPORT void remove(const String&, const ProtectionSpace&); // OS persistent storage. - static Credential getFromPersistentStorage(const ProtectionSpace&); + WEBCORE_EXPORT Credential getFromPersistentStorage(const ProtectionSpace&); + + WEBCORE_EXPORT void clearCredentials(); #if PLATFORM(IOS) - static void saveToPersistentStorage(const ProtectionSpace&, const Credential&); - static void clearCredentials(); + void saveToPersistentStorage(const ProtectionSpace&, const Credential&); #endif // 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 URL&); // Returns true if the URL corresponds to a known protection space, so credentials could be updated. - static Credential get(const URL&); + WEBCORE_EXPORT bool set(const String&, const Credential&, const URL&); // Returns true if the URL corresponds to a known protection space, so credentials could be updated. + WEBCORE_EXPORT Credential get(const String&, const URL&); + +private: + HashMap<std::pair<String /* partitionName */, ProtectionSpace>, Credential> m_protectionSpaceToCredentialMap; + HashSet<String> m_originsWithCredentials; + + typedef HashMap<String, ProtectionSpace> PathToDefaultProtectionSpaceMap; + PathToDefaultProtectionSpaceMap m_pathToDefaultProtectionSpaceMap; - static void setPrivateMode(bool); + PathToDefaultProtectionSpaceMap::iterator findDefaultProtectionSpaceForURL(const URL&); }; } // namespace WebCore diff --git a/Source/WebCore/platform/network/DNS.h b/Source/WebCore/platform/network/DNS.h index c6b748db8..290f40634 100644 --- a/Source/WebCore/platform/network/DNS.h +++ b/Source/WebCore/platform/network/DNS.h @@ -13,7 +13,7 @@ * 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 + * 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 @@ -30,7 +30,7 @@ namespace WebCore { -void prefetchDNS(const String& hostname); +WEBCORE_EXPORT void prefetchDNS(const String& hostname); } #endif diff --git a/Source/WebCore/platform/network/DNSResolveQueue.cpp b/Source/WebCore/platform/network/DNSResolveQueue.cpp index cb051dcef..58773aa33 100644 --- a/Source/WebCore/platform/network/DNSResolveQueue.cpp +++ b/Source/WebCore/platform/network/DNSResolveQueue.cpp @@ -14,7 +14,7 @@ * 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 + * 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 @@ -50,7 +50,7 @@ static const int gMaxRequestsToQueue = 64; // If there were queued names that couldn't be sent simultaneously, check the state of resolvers after this delay. static const double gRetryResolvingInSeconds = 0.1; -DNSResolveQueue& DNSResolveQueue::shared() +DNSResolveQueue& DNSResolveQueue::singleton() { static NeverDestroyed<DNSResolveQueue> queue; @@ -58,22 +58,29 @@ DNSResolveQueue& DNSResolveQueue::shared() } DNSResolveQueue::DNSResolveQueue() - : m_timer(this, &DNSResolveQueue::timerFired) + : m_timer(*this, &DNSResolveQueue::timerFired) , m_requestsInFlight(0) - , m_cachedProxyEnabledStatus(false) + , m_isUsingProxy(true) , m_lastProxyEnabledStatusCheckTime(0) { + // isUsingProxy will return the initial value of m_isUsingProxy at first on + // platforms that have an asynchronous implementation of updateIsUsingProxy, + // so initialize it to true so we won't prefetch before we know if we are using a proxy. } +// 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. bool DNSResolveQueue::isUsingProxy() { double time = monotonicallyIncreasingTime(); static const double minimumProxyCheckDelay = 5; if (time - m_lastProxyEnabledStatusCheckTime > minimumProxyCheckDelay) { m_lastProxyEnabledStatusCheckTime = time; - m_cachedProxyEnabledStatus = platformProxyIsEnabledInSystemPreferences(); + updateIsUsingProxy(); } - return m_cachedProxyEnabledStatus; + return m_isUsingProxy; } void DNSResolveQueue::add(const String& hostname) @@ -98,7 +105,7 @@ void DNSResolveQueue::add(const String& hostname) } } -void DNSResolveQueue::timerFired(Timer<DNSResolveQueue>&) +void DNSResolveQueue::timerFired() { if (isUsingProxy()) { m_names.clear(); diff --git a/Source/WebCore/platform/network/DNSResolveQueue.h b/Source/WebCore/platform/network/DNSResolveQueue.h index de0b6f072..d2a7a77b8 100644 --- a/Source/WebCore/platform/network/DNSResolveQueue.h +++ b/Source/WebCore/platform/network/DNSResolveQueue.h @@ -14,7 +14,7 @@ * 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 + * 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 @@ -39,7 +39,7 @@ class DNSResolveQueue { friend NeverDestroyed<DNSResolveQueue>; public: - static DNSResolveQueue& shared(); + static DNSResolveQueue& singleton(); void add(const String& hostname); void decrementRequestCount() @@ -52,16 +52,16 @@ private: bool isUsingProxy(); - bool platformProxyIsEnabledInSystemPreferences(); + void updateIsUsingProxy(); void platformResolve(const String&); - void timerFired(Timer<DNSResolveQueue>&); + void timerFired(); - Timer<DNSResolveQueue> m_timer; + Timer m_timer; HashSet<String> m_names; std::atomic<int> m_requestsInFlight; - bool m_cachedProxyEnabledStatus; + bool m_isUsingProxy; double m_lastProxyEnabledStatusCheckTime; }; diff --git a/Source/WebCore/platform/network/DataURLDecoder.cpp b/Source/WebCore/platform/network/DataURLDecoder.cpp new file mode 100644 index 000000000..abfb46cec --- /dev/null +++ b/Source/WebCore/platform/network/DataURLDecoder.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2015 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 "DataURLDecoder.h" + +#include "DecodeEscapeSequences.h" +#include "HTTPParsers.h" +#include "SharedBuffer.h" +#include "URL.h" +#include <wtf/MainThread.h> +#include <wtf/RunLoop.h> +#include <wtf/WorkQueue.h> +#include <wtf/text/Base64.h> + +namespace WebCore { +namespace DataURLDecoder { + +static WorkQueue& decodeQueue() +{ + static auto& queue = WorkQueue::create("org.webkit.DataURLDecoder").leakRef(); + return queue; +} + +struct DecodeTask { + WTF_MAKE_FAST_ALLOCATED; +public: + const String urlString; + const StringView encodedData; + const bool isBase64; + const ScheduleContext scheduleContext; + const DecodeCompletionHandler completionHandler; + + Result result; +}; + +#if HAVE(RUNLOOP_TIMER) + +class DecodingResultDispatcher : public ThreadSafeRefCounted<DecodingResultDispatcher> { +public: + static void dispatch(std::unique_ptr<DecodeTask> decodeTask) + { + Ref<DecodingResultDispatcher> dispatcher = adoptRef(*new DecodingResultDispatcher(WTFMove(decodeTask))); + dispatcher->startTimer(); + } + +private: + DecodingResultDispatcher(std::unique_ptr<DecodeTask> decodeTask) + : m_timer(*this, &DecodingResultDispatcher::timerFired) + , m_decodeTask(WTFMove(decodeTask)) + { + } + + void startTimer() + { + // Keep alive until the timer has fired. + ref(); + + auto scheduledPairs = m_decodeTask->scheduleContext.scheduledPairs; + m_timer.startOneShot(0); + m_timer.schedule(scheduledPairs); + } + + void timerFired() + { + if (m_decodeTask->result.data) + m_decodeTask->completionHandler(WTFMove(m_decodeTask->result)); + else + m_decodeTask->completionHandler({ }); + + // Ensure DecodeTask gets deleted in the main thread. + m_decodeTask = nullptr; + + deref(); + } + + RunLoopTimer<DecodingResultDispatcher> m_timer; + std::unique_ptr<DecodeTask> m_decodeTask; +}; + +#endif // HAVE(RUNLOOP_TIMER) + +static Result parseMediaType(const String& mediaType) +{ + auto mimeType = extractMIMETypeFromMediaType(mediaType); + auto charset = extractCharsetFromMediaType(mediaType); + + // https://tools.ietf.org/html/rfc2397 + // If <mediatype> is omitted, it defaults to text/plain;charset=US-ASCII. As a shorthand, + // "text/plain" can be omitted but the charset parameter supplied. + if (mimeType.isEmpty()) { + mimeType = ASCIILiteral("text/plain"); + if (charset.isEmpty()) + charset = ASCIILiteral("US-ASCII"); + } + return { mimeType, charset, !mediaType.isEmpty() ? mediaType : "text/plain;charset=US-ASCII", nullptr }; +} + +static std::unique_ptr<DecodeTask> createDecodeTask(const URL& url, const ScheduleContext& scheduleContext, DecodeCompletionHandler&& completionHandler) +{ + const char dataString[] = "data:"; + const char base64String[] = ";base64"; + + auto urlString = url.string(); + ASSERT(urlString.startsWith(dataString)); + + size_t headerEnd = urlString.find(',', strlen(dataString)); + size_t encodedDataStart = headerEnd == notFound ? headerEnd : headerEnd + 1; + + auto encodedData = StringView(urlString).substring(encodedDataStart); + auto header = StringView(urlString).substring(strlen(dataString), headerEnd - strlen(dataString)); + bool isBase64 = header.endsWithIgnoringASCIICase(StringView(base64String)); + auto mediaType = (isBase64 ? header.substring(0, header.length() - strlen(base64String)) : header).toString(); + + return std::make_unique<DecodeTask>(DecodeTask { + urlString.isolatedCopy(), + WTFMove(encodedData), + isBase64, + scheduleContext, + WTFMove(completionHandler), + parseMediaType(mediaType) + }); +} + +static void decodeBase64(DecodeTask& task) +{ + Vector<char> buffer; + // First try base64url. + if (!base64URLDecode(task.encodedData.toStringWithoutCopying(), buffer)) { + // Didn't work, try unescaping and decoding as base64. + auto unescapedString = decodeURLEscapeSequences(task.encodedData.toStringWithoutCopying()); + if (!base64Decode(unescapedString, buffer, Base64IgnoreSpacesAndNewLines)) + return; + } + buffer.shrinkToFit(); + task.result.data = SharedBuffer::adoptVector(buffer); +} + +static void decodeEscaped(DecodeTask& task) +{ + TextEncoding encodingFromCharset(task.result.charset); + auto& encoding = encodingFromCharset.isValid() ? encodingFromCharset : UTF8Encoding(); + auto buffer = decodeURLEscapeSequencesAsData(task.encodedData, encoding); + + buffer.shrinkToFit(); + task.result.data = SharedBuffer::adoptVector(buffer); +} + +void decode(const URL& url, const ScheduleContext& scheduleContext, DecodeCompletionHandler&& completionHandler) +{ + ASSERT(url.protocolIsData()); + + decodeQueue().dispatch([decodeTask = createDecodeTask(url, scheduleContext, WTFMove(completionHandler))]() mutable { + if (decodeTask->isBase64) + decodeBase64(*decodeTask); + else + decodeEscaped(*decodeTask); + +#if HAVE(RUNLOOP_TIMER) + DecodingResultDispatcher::dispatch(WTFMove(decodeTask)); +#else + callOnMainThread([decodeTask = WTFMove(decodeTask)] { + if (!decodeTask->result.data) { + decodeTask->completionHandler({ }); + return; + } + decodeTask->completionHandler(WTFMove(decodeTask->result)); + }); +#endif + }); +} + +} +} diff --git a/Source/WebCore/platform/network/DataURLDecoder.h b/Source/WebCore/platform/network/DataURLDecoder.h new file mode 100644 index 000000000..e5db15426 --- /dev/null +++ b/Source/WebCore/platform/network/DataURLDecoder.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 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 DataURLDecoder_h +#define DataURLDecoder_h + + +#include <functional> +#include <wtf/Optional.h> +#include <wtf/text/WTFString.h> + +#if HAVE(RUNLOOP_TIMER) +#include <wtf/RunLoopTimer.h> +#endif + +namespace WebCore { + +class SharedBuffer; +class URL; + +namespace DataURLDecoder { + +struct Result { + String mimeType; + String charset; + String contentType; + RefPtr<SharedBuffer> data; +}; + +using DecodeCompletionHandler = std::function<void (std::optional<Result>)>; +struct ScheduleContext { +#if HAVE(RUNLOOP_TIMER) + SchedulePairHashSet scheduledPairs; +#endif +}; + +void decode(const URL&, const ScheduleContext&, DecodeCompletionHandler&&); + +} + +} + +#endif diff --git a/Source/WebCore/platform/network/FormData.cpp b/Source/WebCore/platform/network/FormData.cpp index f056b58ca..564a05379 100644 --- a/Source/WebCore/platform/network/FormData.cpp +++ b/Source/WebCore/platform/network/FormData.cpp @@ -20,10 +20,8 @@ */ #include "config.h" - #include "FormData.h" -#include "BlobData.h" #include "BlobRegistryImpl.h" #include "BlobURL.h" #include "Chrome.h" @@ -33,20 +31,12 @@ #include "FileSystem.h" #include "FormDataBuilder.h" #include "FormDataList.h" -#include "KeyedCoding.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) - , m_containsPasswordData(false) { } @@ -54,18 +44,15 @@ inline FormData::FormData(const FormData& data) : RefCounted<FormData>() , m_elements(data.m_elements) , m_identifier(data.m_identifier) - , m_hasGeneratedFiles(false) , m_alwaysStream(false) , m_containsPasswordData(data.m_containsPasswordData) { // 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(); + for (FormDataElement& element : m_elements) { + if (element.m_type == FormDataElement::Type::EncodedFile) { + element.m_generatedFilename = String(); + element.m_ownsGeneratedFile = false; } } } @@ -74,84 +61,81 @@ 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); + ASSERT(!hasOwnedGeneratedFiles()); removeGeneratedFilesIfNeeded(); } -PassRefPtr<FormData> FormData::create() +Ref<FormData> FormData::create() { - return adoptRef(new FormData); + return adoptRef(*new FormData); } -PassRefPtr<FormData> FormData::create(const void* data, size_t size) +Ref<FormData> FormData::create(const void* data, size_t size) { - RefPtr<FormData> result = create(); + Ref<FormData> result = create(); result->appendData(data, size); - return result.release(); + return result; } -PassRefPtr<FormData> FormData::create(const CString& string) +Ref<FormData> FormData::create(const CString& string) { - RefPtr<FormData> result = create(); + Ref<FormData> result = create(); result->appendData(string.data(), string.length()); - return result.release(); + return result; } -PassRefPtr<FormData> FormData::create(const Vector<char>& vector) +Ref<FormData> FormData::create(const Vector<char>& vector) { - RefPtr<FormData> result = create(); + Ref<FormData> result = create(); result->appendData(vector.data(), vector.size()); - return result.release(); + return result; } -PassRefPtr<FormData> FormData::create(const FormDataList& list, const TextEncoding& encoding, EncodingType encodingType) +Ref<FormData> FormData::create(const FormDataList& list, const TextEncoding& encoding, EncodingType encodingType) { - RefPtr<FormData> result = create(); + Ref<FormData> result = create(); result->appendKeyValuePairItems(list, encoding, false, 0, encodingType); - return result.release(); + return result; } -PassRefPtr<FormData> FormData::createMultiPart(const FormDataList& list, const TextEncoding& encoding, Document* document) +Ref<FormData> FormData::createMultiPart(const FormDataList& list, const TextEncoding& encoding, Document* document) { - RefPtr<FormData> result = create(); + Ref<FormData> result = create(); result->appendKeyValuePairItems(list, encoding, true, document); - return result.release(); + return result; } -PassRefPtr<FormData> FormData::copy() const +Ref<FormData> FormData::copy() const { - return adoptRef(new FormData(*this)); + return adoptRef(*new FormData(*this)); } -PassRefPtr<FormData> FormData::deepCopy() const +Ref<FormData> FormData::isolatedCopy() const { - RefPtr<FormData> formData(create()); + // FIXME: isolatedCopy() (historically deepCopy()) only copies certain values from `this`. Why is that? + auto 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.uncheckedAppend(FormDataElement(e.m_data)); - break; - case FormDataElement::encodedFile: -#if ENABLE(BLOB) - formData->m_elements.uncheckedAppend(FormDataElement(e.m_filename, e.m_fileStart, e.m_fileLength, e.m_expectedFileModificationTime, e.m_shouldGenerateFile)); -#else - formData->m_elements.uncheckedAppend(FormDataElement(e.m_filename, e.m_shouldGenerateFile)); -#endif - break; -#if ENABLE(BLOB) - case FormDataElement::encodedBlob: - formData->m_elements.uncheckedAppend(FormDataElement(e.m_url)); - break; -#endif - } + formData->m_elements.reserveInitialCapacity(m_elements.size()); + for (auto& element : m_elements) + formData->m_elements.uncheckedAppend(element.isolatedCopy()); + + return formData; +} + +FormDataElement FormDataElement::isolatedCopy() const +{ + switch (m_type) { + case Type::Data: + return FormDataElement(m_data); + case Type::EncodedFile: + return FormDataElement(m_filename.isolatedCopy(), m_fileStart, m_fileLength, m_expectedFileModificationTime, m_shouldGenerateFile); + case Type::EncodedBlob: + return FormDataElement(m_url.isolatedCopy()); } - return formData.release(); + + RELEASE_ASSERT_NOT_REACHED(); } void FormData::appendData(const void* data, size_t size) @@ -161,14 +145,9 @@ void FormData::appendData(const void* data, size_t size) void FormData::appendFile(const String& filename, bool shouldGenerateFile) { -#if ENABLE(BLOB) m_elements.append(FormDataElement(filename, 0, BlobDataItem::toEndOfFile, invalidFileTime(), 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)); @@ -178,7 +157,6 @@ void FormData::appendBlob(const URL& blobURL) { m_elements.append(FormDataElement(blobURL)); } -#endif void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncoding& encoding, bool isMultiPartForm, Document* document, EncodingType encodingType) { @@ -202,16 +180,11 @@ void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncod // If the current type is blob, then we also need to include the filename if (value.blob()) { String name; - if (value.blob()->isFile()) { - File* file = toFile(value.blob()); - // For file blob, use the filename (or relative path if it is present) as the name. -#if ENABLE(DIRECTORY_UPLOAD) - name = file->webkitRelativePath().isEmpty() ? file->name() : file->webkitRelativePath(); -#else - name = file->name(); -#endif + if (is<File>(*value.blob())) { + File& file = downcast<File>(*value.blob()); + name = file.name(); // Let the application specify a filename if it's going to generate a replacement file for the upload. - const String& path = file->path(); + const String& path = file.path(); if (!path.isEmpty()) { if (Page* page = document->page()) { String generatedFileName; @@ -248,27 +221,19 @@ void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncod // Append body appendData(header.data(), header.size()); if (value.blob()) { - if (value.blob()->isFile()) { - File* file = toFile(value.blob()); + if (is<File>(*value.blob())) { + File& file = downcast<File>(*value.blob()); // Do not add the file if the path is empty. - if (!file->path().isEmpty()) - appendFile(file->path(), shouldGenerateFile); + if (!file.path().isEmpty()) + appendFile(file.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(), encodingType); - } + } else + FormDataBuilder::addKeyValuePairAsFormData(encodedData, key.data(), value.data(), encodingType); } if (isMultiPartForm) @@ -279,7 +244,7 @@ void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncod char* FormData::expandDataStore(size_t size) { - if (m_elements.isEmpty() || m_elements.last().m_type != FormDataElement::data) + if (m_elements.isEmpty() || m_elements.last().m_type != FormDataElement::Type::Data) m_elements.append(FormDataElement()); FormDataElement& e = m_elements.last(); size_t oldSize = e.m_data.size(); @@ -294,7 +259,7 @@ void FormData::flatten(Vector<char>& data) const 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) + if (e.m_type == FormDataElement::Type::Data) data.append(e.m_data.data(), static_cast<size_t>(e.m_data.size())); } } @@ -306,14 +271,13 @@ String FormData::flattenToString() const return Latin1Encoding().decode(reinterpret_cast<const char*>(bytes.data()), bytes.size()); } -#if ENABLE(BLOB) static void appendBlobResolved(FormData* formData, const URL& url) { if (!blobRegistry().isBlobRegistryImpl()) { LOG_ERROR("Tried to resolve a blob without a usable registry"); return; } - BlobStorageData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(URL(ParsedURLString, url)); + BlobData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(url); if (!blobData) { LOG_ERROR("Could not get blob data from a registry"); return; @@ -323,284 +287,103 @@ static void appendBlobResolved(FormData* formData, const URL& url) const BlobDataItemList::const_iterator itend = blobData->items().end(); for (; it != itend; ++it) { const BlobDataItem& blobItem = *it; - if (blobItem.type == BlobDataItem::Data) - formData->appendData(blobItem.data->data() + static_cast<int>(blobItem.offset), static_cast<int>(blobItem.length)); - else if (blobItem.type == BlobDataItem::File) - formData->appendFileRange(blobItem.path, blobItem.offset, blobItem.length, blobItem.expectedModificationTime); - else if (blobItem.type == BlobDataItem::Blob) - appendBlobResolved(formData, blobItem.url); + if (blobItem.type() == BlobDataItem::Type::Data) { + ASSERT(blobItem.data().data()); + formData->appendData(blobItem.data().data()->data() + static_cast<int>(blobItem.offset()), static_cast<int>(blobItem.length())); + } else if (blobItem.type() == BlobDataItem::Type::File) + formData->appendFileRange(blobItem.file()->path(), blobItem.offset(), blobItem.length(), blobItem.file()->expectedModificationTime()); else ASSERT_NOT_REACHED(); } } -PassRefPtr<FormData> FormData::resolveBlobReferences() +Ref<FormData> FormData::resolveBlobReferences() { // First check if any blobs needs to be resolved, or we can take the fast path. bool hasBlob = false; Vector<FormDataElement>::const_iterator it = elements().begin(); const Vector<FormDataElement>::const_iterator itend = elements().end(); for (; it != itend; ++it) { - if (it->m_type == FormDataElement::encodedBlob) { + if (it->m_type == FormDataElement::Type::EncodedBlob) { hasBlob = true; break; } } if (!hasBlob) - return this; + return *this; // Create a copy to append the result into. - RefPtr<FormData> newFormData = FormData::create(); + Ref<FormData> newFormData = FormData::create(); newFormData->setAlwaysStream(alwaysStream()); newFormData->setIdentifier(identifier()); it = elements().begin(); for (; it != itend; ++it) { const FormDataElement& element = *it; - if (element.m_type == FormDataElement::data) + if (element.m_type == FormDataElement::Type::Data) newFormData->appendData(element.m_data.data(), element.m_data.size()); - else if (element.m_type == FormDataElement::encodedFile) + else if (element.m_type == FormDataElement::Type::EncodedFile) newFormData->appendFileRange(element.m_filename, element.m_fileStart, element.m_fileLength, element.m_expectedFileModificationTime, element.m_shouldGenerateFile); - else if (element.m_type == FormDataElement::encodedBlob) - appendBlobResolved(newFormData.get(), element.m_url); + else if (element.m_type == FormDataElement::Type::EncodedBlob) + appendBlobResolved(newFormData.ptr(), element.m_url); else ASSERT_NOT_REACHED(); } - return newFormData.release(); + return newFormData; } -#endif void FormData::generateFiles(Document* document) { - ASSERT(!m_hasGeneratedFiles); - - if (m_hasGeneratedFiles) - return; - Page* page = document->page(); if (!page) 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_shouldGenerateFile) { - e.m_generatedFilename = page->chrome().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(); + for (FormDataElement& element : m_elements) { + if (element.m_type == FormDataElement::Type::EncodedFile && element.m_shouldGenerateFile) { + ASSERT(!element.m_ownsGeneratedFile); + ASSERT(element.m_generatedFilename.isEmpty()); + if (!element.m_generatedFilename.isEmpty()) + continue; + element.m_generatedFilename = page->chrome().client().generateReplacementFile(element.m_filename); + if (!element.m_generatedFilename.isEmpty()) + element.m_ownsGeneratedFile = true; } } - m_hasGeneratedFiles = false; } -static void encodeElement(Encoder& encoder, const FormDataElement& element) +bool FormData::hasGeneratedFiles() const { - 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.encodeString(element.m_generatedFilename); - 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(invalidFileTime()); -#endif - return; - -#if ENABLE(BLOB) - case FormDataElement::encodedBlob: - encoder.encodeString(element.m_url.string()); - return; -#endif + for (const FormDataElement& element : m_elements) { + if (element.m_type == FormDataElement::Type::EncodedFile && !element.m_generatedFilename.isEmpty()) + return true; } - - ASSERT_NOT_REACHED(); -} - -static void encodeElement(KeyedEncoder& encoder, const FormDataElement& element) -{ - encoder.encodeEnum("type", element.m_type); - - switch (element.m_type) { - case FormDataElement::data: - encoder.encodeBytes("data", reinterpret_cast<const uint8_t*>(element.m_data.data()), element.m_data.size()); - return; - case FormDataElement::encodedFile: - encoder.encodeString("filename", element.m_filename); - encoder.encodeString("generatedFilename", element.m_generatedFilename); - encoder.encodeBool("shouldGenerateFile", element.m_shouldGenerateFile); -#if ENABLE(BLOB) - encoder.encodeInt64("fileStart", element.m_fileStart); - encoder.encodeInt64("fileLength", element.m_fileLength); - encoder.encodeDouble("expectedFileModificationTime", element.m_expectedFileModificationTime); -#endif - return; - -#if ENABLE(BLOB) - case FormDataElement::encodedBlob: - encoder.encodeString("url", element.m_url.string()); - return; -#endif - } - - ASSERT_NOT_REACHED(); + return false; } -static bool decodeElement(Decoder& decoder, FormDataElement& element) +bool FormData::hasOwnedGeneratedFiles() const { - uint32_t type; - if (!decoder.decodeUInt32(type)) - return false; - - 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 = static_cast<FormDataElement::Type>(type); - String filenameOrURL; - if (!decoder.decodeString(filenameOrURL)) - return false; - if (type == FormDataElement::encodedFile) { - if (!decoder.decodeString(element.m_generatedFilename)) - return false; - if (!decoder.decodeBool(element.m_shouldGenerateFile)) - return false; + for (const FormDataElement& element : m_elements) { + if (element.m_type == FormDataElement::Type::EncodedFile && element.m_ownsGeneratedFile) { + ASSERT(!element.m_generatedFilename.isEmpty()); + return true; } - 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 != BlobDataItem::toEndOfFile && fileLength < fileStart) - return false; - double expectedFileModificationTime; - if (!decoder.decodeDouble(expectedFileModificationTime)) - return false; - - element.m_filename = filenameOrURL; -#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_url = URL(URL(), blobURLString); - return true; -#endif - - } - return false; } -void FormData::encode(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) - encodeElement(encoder, m_elements[i]); - - encoder.encodeBool(m_hasGeneratedFiles); - - encoder.encodeInt64(m_identifier); -} - -void FormData::encode(KeyedEncoder& encoder) const -{ - encoder.encodeBool("alwaysStream", m_alwaysStream); - encoder.encodeBytes("boundary", reinterpret_cast<const uint8_t*>(m_boundary.data()), m_boundary.size()); - - encoder.encodeObjects("elements", m_elements.begin(), m_elements.end(), [](KeyedEncoder& encoder, const FormDataElement& element) { - encodeElement(encoder, element); - }); - - encoder.encodeBool("hasGeneratedFiles", m_hasGeneratedFiles); - encoder.encodeInt64("identifier", m_identifier); -} - -PassRefPtr<FormData> FormData::decode(Decoder& decoder) +void FormData::removeGeneratedFilesIfNeeded() { - 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 (!decodeElement(decoder, element)) - return 0; - data->m_elements.append(element); + for (FormDataElement& element : m_elements) { + if (element.m_type == FormDataElement::Type::EncodedFile && element.m_ownsGeneratedFile) { + ASSERT(!element.m_generatedFilename.isEmpty()); + ASSERT(element.m_shouldGenerateFile); + String directory = directoryName(element.m_generatedFilename); + deleteFile(element.m_generatedFilename); + deleteEmptyDirectory(directory); + element.m_generatedFilename = String(); + element.m_ownsGeneratedFile = false; + } } - - 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 index e3761f8ba..7e44dcaf9 100644 --- a/Source/WebCore/platform/network/FormData.h +++ b/Source/WebCore/platform/network/FormData.h @@ -20,6 +20,7 @@ #ifndef FormData_h #define FormData_h +#include "BlobData.h" #include "URL.h" #include <wtf/Forward.h> #include <wtf/RefCounted.h> @@ -30,38 +31,65 @@ namespace WebCore { class Document; class FormDataList; -class KeyedEncoder; 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 URL& blobURL) : m_type(encodedBlob), m_url(blobURL) { } -#else - FormDataElement(const String& filename, bool shouldGenerateFile) : m_type(encodedFile), m_filename(filename), m_shouldGenerateFile(shouldGenerateFile) { } -#endif + enum class Type { + Data, + EncodedFile, + EncodedBlob, + }; - enum Type { - data, - encodedFile -#if ENABLE(BLOB) - , encodedBlob -#endif - } m_type; + FormDataElement() + : m_type(Type::Data) + { + } + + explicit FormDataElement(const Vector<char>& array) + : m_type(Type::Data) + , m_data(array) + { + } + + FormDataElement(const String& filename, long long fileStart, long long fileLength, double expectedFileModificationTime, bool shouldGenerateFile) + : m_type(Type::EncodedFile) + , m_filename(filename) + , m_fileStart(fileStart) + , m_fileLength(fileLength) + , m_expectedFileModificationTime(expectedFileModificationTime) + , m_shouldGenerateFile(shouldGenerateFile) + , m_ownsGeneratedFile(false) + { + } + + explicit FormDataElement(const URL& blobURL) + : m_type(Type::EncodedBlob) + , m_url(blobURL) + { + } + + FormDataElement isolatedCopy() const; + + template<typename Encoder> + void encode(Encoder&) const; + template<typename Decoder> + static bool decode(Decoder&, FormDataElement& result); + + Type m_type; Vector<char> m_data; String m_filename; -#if ENABLE(BLOB) URL m_url; // For Blob or URL. - long long m_fileStart; - long long m_fileLength; + int64_t m_fileStart; + int64_t m_fileLength; double m_expectedFileModificationTime; -#endif + // FIXME: Generated file support in FormData is almost identical to Blob, they should be merged. + // We can't just switch to using Blobs for all files for two reasons: + // 1. Not all platforms enable BLOB support. + // 2. EncodedFile form data elements do not have a valid m_expectedFileModificationTime, meaning that we always upload the latest content from disk. String m_generatedFilename; bool m_shouldGenerateFile; + bool m_ownsGeneratedFile; }; inline bool operator==(const FormDataElement& a, const FormDataElement& b) @@ -71,16 +99,12 @@ inline bool operator==(const FormDataElement& a, const FormDataElement& b) if (a.m_type != b.m_type) return false; - if (a.m_type == FormDataElement::data) + if (a.m_type == FormDataElement::Type::Data) return a.m_data == b.m_data; - if (a.m_type == FormDataElement::encodedFile) -#if ENABLE(BLOB) + if (a.m_type == FormDataElement::Type::EncodedFile) 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) + if (a.m_type == FormDataElement::Type::EncodedBlob) return a.m_url == b.m_url; -#else - return a.m_filename == b.m_filename; -#endif return true; } @@ -90,6 +114,80 @@ inline bool operator!=(const FormDataElement& a, const FormDataElement& b) return !(a == b); } + +template<typename Encoder> +void FormDataElement::encode(Encoder& encoder) const +{ + encoder.encodeEnum(m_type); + + switch (m_type) { + case Type::Data: + encoder << m_data; + break; + + case Type::EncodedFile: + encoder << m_filename; + encoder << m_generatedFilename; + encoder << m_shouldGenerateFile; + encoder << m_fileStart; + encoder << m_fileLength; + encoder << m_expectedFileModificationTime; + break; + + case Type::EncodedBlob: + encoder << m_url.string(); + break; + } +} + +template<typename Decoder> +bool FormDataElement::decode(Decoder& decoder, FormDataElement& result) +{ + if (!decoder.decodeEnum(result.m_type)) + return false; + + switch (result.m_type) { + case Type::Data: + if (!decoder.decode(result.m_data)) + return false; + + return true; + + case Type::EncodedFile: + if (!decoder.decode(result.m_filename)) + return false; + if (!decoder.decode(result.m_generatedFilename)) + return false; + if (!decoder.decode(result.m_shouldGenerateFile)) + return false; + result.m_ownsGeneratedFile = false; + if (!decoder.decode(result.m_fileStart)) + return false; + if (!decoder.decode(result.m_fileLength)) + return false; + + if (result.m_fileLength != BlobDataItem::toEndOfFile && result.m_fileLength < result.m_fileStart) + return false; + + if (!decoder.decode(result.m_expectedFileModificationTime)) + return false; + + return true; + + case Type::EncodedBlob: { + String blobURLString; + if (!decoder.decode(blobURLString)) + return false; + + result.m_url = URL(URL(), blobURLString); + + return true; + } + } + + return false; +} + class FormData : public RefCounted<FormData> { public: enum EncodingType { @@ -98,36 +196,36 @@ public: MultipartFormData // for multipart/form-data }; - 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&, EncodingType = FormURLEncoded); - static PassRefPtr<FormData> createMultiPart(const FormDataList&, const TextEncoding&, Document*); - PassRefPtr<FormData> copy() const; - PassRefPtr<FormData> deepCopy() const; - ~FormData(); + WEBCORE_EXPORT static Ref<FormData> create(); + WEBCORE_EXPORT static Ref<FormData> create(const void*, size_t); + static Ref<FormData> create(const CString&); + static Ref<FormData> create(const Vector<char>&); + static Ref<FormData> create(const FormDataList&, const TextEncoding&, EncodingType = FormURLEncoded); + static Ref<FormData> createMultiPart(const FormDataList&, const TextEncoding&, Document*); + WEBCORE_EXPORT ~FormData(); + // FIXME: Both these functions perform a deep copy of m_elements, but differ in handling of other data members. + // How much of that is intentional? We need better names that explain the difference. + Ref<FormData> copy() const; + Ref<FormData> isolatedCopy() const; + + template<typename Encoder> void encode(Encoder&) const; - void encode(KeyedEncoder&) const; - static PassRefPtr<FormData> decode(Decoder&); + template<typename Decoder> + static RefPtr<FormData> decode(Decoder&); - void appendData(const void* data, size_t); + WEBCORE_EXPORT 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 URL& blobURL); -#endif + WEBCORE_EXPORT void appendFileRange(const String& filename, long long start, long long length, double expectedModificationTime, bool shouldGenerateFile = false); + WEBCORE_EXPORT void appendBlob(const URL& blobURL); char* expandDataStore(size_t); void flatten(Vector<char>&) const; // omits files String flattenToString() const; // omits files -#if ENABLE(BLOB) // Resolve all blob references so we only have file and data. // If the FormData has no blob references to resolve, this is returned. - PassRefPtr<FormData> resolveBlobReferences(); -#endif + Ref<FormData> resolveBlobReferences(); bool isEmpty() const { return m_elements.isEmpty(); } const Vector<FormDataElement>& elements() const { return m_elements; } @@ -149,9 +247,9 @@ public: static EncodingType parseEncodingType(const String& type) { - if (equalIgnoringCase(type, "text/plain")) + if (equalLettersIgnoringASCIICase(type, "text/plain")) return TextPlain; - if (equalIgnoringCase(type, "multipart/form-data")) + if (equalLettersIgnoringASCIICase(type, "multipart/form-data")) return MultipartFormData; return FormURLEncoded; } @@ -162,13 +260,15 @@ private: void appendKeyValuePairItems(const FormDataList&, const TextEncoding&, bool isMultiPartForm, Document*, EncodingType = FormURLEncoded); + bool hasGeneratedFiles() const; + bool hasOwnedGeneratedFiles() const; + Vector<FormDataElement> m_elements; - int64_t m_identifier; - bool m_hasGeneratedFiles; - bool m_alwaysStream; + int64_t m_identifier { 0 }; + bool m_alwaysStream { false }; Vector<char> m_boundary; - bool m_containsPasswordData; + bool m_containsPasswordData { false }; }; inline bool operator==(const FormData& a, const FormData& b) @@ -181,6 +281,35 @@ inline bool operator!=(const FormData& a, const FormData& b) return !(a == b); } +template<typename Encoder> +void FormData::encode(Encoder& encoder) const +{ + encoder << m_alwaysStream; + encoder << m_boundary; + encoder << m_elements; + encoder << m_identifier; +} + +template<typename Decoder> +RefPtr<FormData> FormData::decode(Decoder& decoder) +{ + RefPtr<FormData> data = FormData::create(); + + if (!decoder.decode(data->m_alwaysStream)) + return nullptr; + + if (!decoder.decode(data->m_boundary)) + return nullptr; + + if (!decoder.decode(data->m_elements)) + return nullptr; + + if (!decoder.decode(data->m_identifier)) + return nullptr; + + return data; +} + } // namespace WebCore #endif diff --git a/Source/WebCore/platform/network/FormDataBuilder.cpp b/Source/WebCore/platform/network/FormDataBuilder.cpp index a76916ebd..6888b2cce 100644 --- a/Source/WebCore/platform/network/FormDataBuilder.cpp +++ b/Source/WebCore/platform/network/FormDataBuilder.cpp @@ -26,14 +26,13 @@ #include "FormDataBuilder.h" #include "Blob.h" -#include "Document.h" #include "TextEncoding.h" - #include <limits> #include <wtf/Assertions.h> #include <wtf/HexNumber.h> -#include <wtf/text/CString.h> #include <wtf/RandomNumber.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringView.h> namespace WebCore { @@ -61,12 +60,11 @@ static void appendQuotedString(Vector<char>& buffer, const CString& string) size_t length = string.length(); for (size_t i = 0; i < length; ++i) { char c = string.data()[i]; - switch (c) { - case 0x0a: + case 0xA: append(buffer, "%0A"); break; - case 0x0d: + case 0xD: append(buffer, "%0D"); break; case '"': @@ -78,25 +76,6 @@ static void appendQuotedString(Vector<char>& buffer, const CString& string) } } -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; - } - - return document.inputEncoding(); -} - Vector<char> FormDataBuilder::generateUniqueBoundaryString() { Vector<char> boundary; @@ -165,7 +144,7 @@ void FormDataBuilder::addFilenameToMultiPartHeader(Vector<char>& buffer, const T // 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.deprecatedCharacters(), filename.length(), QuestionMarksForUnencodables)); + appendQuotedString(buffer, encoding.encode(filename, QuestionMarksForUnencodables)); append(buffer, '"'); } @@ -208,7 +187,7 @@ void FormDataBuilder::encodeStringAsFormData(Vector<char>& buffer, const CString 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)) + if (isASCIIAlphanumeric(c) || strchr(safeCharacters, c)) append(buffer, c); else if (c == ' ') append(buffer, '+'); diff --git a/Source/WebCore/platform/network/FormDataBuilder.h b/Source/WebCore/platform/network/FormDataBuilder.h index 77b59d150..7adecaa66 100644 --- a/Source/WebCore/platform/network/FormDataBuilder.h +++ b/Source/WebCore/platform/network/FormDataBuilder.h @@ -26,14 +26,11 @@ namespace WebCore { -class Document; class TextEncoding; class FormDataBuilder { WTF_MAKE_NONCOPYABLE(FormDataBuilder); public: - static TextEncoding encodingFromAcceptCharset(const String& acceptCharset, Document&); - // Helper functions used by HTMLFormElement for multi-part form data static Vector<char> generateUniqueBoundaryString(); static void beginMultiPartHeader(Vector<char>&, const CString& boundary, const CString& name); diff --git a/Source/WebCore/platform/network/HTTPHeaderMap.cpp b/Source/WebCore/platform/network/HTTPHeaderMap.cpp index a4593b5e2..196948010 100644 --- a/Source/WebCore/platform/network/HTTPHeaderMap.cpp +++ b/Source/WebCore/platform/network/HTTPHeaderMap.cpp @@ -32,6 +32,7 @@ #include "HTTPHeaderMap.h" #include <utility> +#include <wtf/text/StringView.h> namespace WebCore { @@ -39,76 +40,115 @@ HTTPHeaderMap::HTTPHeaderMap() { } -HTTPHeaderMap::~HTTPHeaderMap() +HTTPHeaderMap HTTPHeaderMap::isolatedCopy() const { + HTTPHeaderMap map; + + for (auto& header : m_commonHeaders) + map.m_commonHeaders.set(header.key, header.value.isolatedCopy()); + + for (auto& header : m_uncommonHeaders) + map.m_uncommonHeaders.set(header.key.isolatedCopy(), header.value.isolatedCopy()); + + return map; } -PassOwnPtr<CrossThreadHTTPHeaderMapData> HTTPHeaderMap::copyData() const +String HTTPHeaderMap::get(const String& name) const { - OwnPtr<CrossThreadHTTPHeaderMapData> data = adoptPtr(new CrossThreadHTTPHeaderMapData()); - data->reserveInitialCapacity(size()); + HTTPHeaderName headerName; + if (!findHTTPHeaderName(name, headerName)) + return m_uncommonHeaders.get(name); + return m_commonHeaders.get(headerName); +} + +#if USE(CF) - HTTPHeaderMap::const_iterator end_it = end(); - for (HTTPHeaderMap::const_iterator it = begin(); it != end_it; ++it) - data->uncheckedAppend(std::make_pair(it->key.string().isolatedCopy(), it->value.isolatedCopy())); +void HTTPHeaderMap::set(CFStringRef name, const String& value) +{ + // Fast path: avoid constructing a temporary String in the common header case. + if (auto* nameCharacters = CFStringGetCStringPtr(name, kCFStringEncodingASCII)) { + unsigned length = CFStringGetLength(name); + HTTPHeaderName headerName; + if (findHTTPHeaderName(StringView(reinterpret_cast<const LChar*>(nameCharacters), length), headerName)) + m_commonHeaders.set(headerName, value); + else + m_uncommonHeaders.set(String(nameCharacters, length), value); + return; + } - return data.release(); + set(String(name), value); } -void HTTPHeaderMap::adopt(PassOwnPtr<CrossThreadHTTPHeaderMapData> data) +#endif // USE(CF) + +void HTTPHeaderMap::set(const String& name, const String& value) { - clear(); - size_t dataSize = data->size(); - for (size_t index = 0; index < dataSize; ++index) { - std::pair<String, String>& header = (*data)[index]; - set(header.first, header.second); + HTTPHeaderName headerName; + if (!findHTTPHeaderName(name, headerName)) { + m_uncommonHeaders.set(name, value); + return; } + m_commonHeaders.set(headerName, value); } -String HTTPHeaderMap::get(const AtomicString& name) const +void HTTPHeaderMap::add(const String& name, const String& value) { - return HashMap<AtomicString, String, CaseFoldingHash>::get(name); + HTTPHeaderName headerName; + if (!findHTTPHeaderName(name, headerName)) { + auto result = m_uncommonHeaders.add(name, value); + if (!result.isNewEntry) + result.iterator->value = result.iterator->value + ',' + value; + return; + } + add(headerName, value); } -HTTPHeaderMap::AddResult HTTPHeaderMap::add(const AtomicString& name, const String& value) +bool HTTPHeaderMap::addIfNotPresent(HTTPHeaderName headerName, const String& value) { - return HashMap<AtomicString, String, CaseFoldingHash>::add(name, value); + return m_commonHeaders.add(headerName, value).isNewEntry; } -// 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); - } -}; +bool HTTPHeaderMap::contains(const String& name) const +{ + HTTPHeaderName headerName; + if (findHTTPHeaderName(name, headerName)) + return contains(headerName); + return m_uncommonHeaders.contains(name); +} + +bool HTTPHeaderMap::remove(const String& name) +{ + HTTPHeaderName headerName; + if (findHTTPHeaderName(name, headerName)) + return remove(headerName); + return m_uncommonHeaders.remove(name); +} + +String HTTPHeaderMap::get(HTTPHeaderName name) const +{ + return m_commonHeaders.get(name); +} + +void HTTPHeaderMap::set(HTTPHeaderName name, const String& value) +{ + m_commonHeaders.set(name, value); +} -String HTTPHeaderMap::get(const char* name) const +bool HTTPHeaderMap::contains(HTTPHeaderName name) const { - const_iterator i = find<CaseFoldingCStringTranslator>(name); - if (i == end()) - return String(); - return i->value; + return m_commonHeaders.contains(name); } - -bool HTTPHeaderMap::contains(const char* name) const + +bool HTTPHeaderMap::remove(HTTPHeaderName name) { - return find<CaseFoldingCStringTranslator>(name) != end(); + return m_commonHeaders.remove(name); } -HTTPHeaderMap::AddResult HTTPHeaderMap::add(const char* name, const String& value) +void HTTPHeaderMap::add(HTTPHeaderName name, const String& value) { - return HashMap<AtomicString, String, CaseFoldingHash>::add<CaseFoldingCStringTranslator>(name, value); + auto result = m_commonHeaders.add(name, value); + if (!result.isNewEntry) + result.iterator->value = result.iterator->value + ',' + value; } } // namespace WebCore diff --git a/Source/WebCore/platform/network/HTTPHeaderMap.h b/Source/WebCore/platform/network/HTTPHeaderMap.h index f4e678747..64559e125 100644 --- a/Source/WebCore/platform/network/HTTPHeaderMap.h +++ b/Source/WebCore/platform/network/HTTPHeaderMap.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -24,43 +24,191 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef HTTPHeaderMap_h -#define HTTPHeaderMap_h +#pragma once +#include "HTTPHeaderNames.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/Optional.h> #include <wtf/text/StringHash.h> namespace WebCore { - typedef Vector<std::pair<String, String>> CrossThreadHTTPHeaderMapData; +// FIXME: Not every header fits into a map. Notably, multiple Set-Cookie header fields are needed to set multiple cookies. - // FIXME: Not every header fits into a map. Notably, multiple Set-Cookie header fields are needed to set multiple cookies. - class HTTPHeaderMap : public HashMap<AtomicString, String, CaseFoldingHash> { +class HTTPHeaderMap { +public: + typedef HashMap<HTTPHeaderName, String, WTF::IntHash<HTTPHeaderName>, WTF::StrongEnumHashTraits<HTTPHeaderName>> CommonHeadersHashMap; + typedef HashMap<String, String, ASCIICaseInsensitiveHash> UncommonHeadersHashMap; + + class HTTPHeaderMapConstIterator { public: - HTTPHeaderMap(); - ~HTTPHeaderMap(); + HTTPHeaderMapConstIterator(const HTTPHeaderMap& table, CommonHeadersHashMap::const_iterator commonHeadersIt, UncommonHeadersHashMap::const_iterator uncommonHeadersIt) + : m_table(table) + , m_commonHeadersIt(commonHeadersIt) + , m_uncommonHeadersIt(uncommonHeadersIt) + { + if (!updateKeyValue(m_commonHeadersIt)) + updateKeyValue(m_uncommonHeadersIt); + } + + struct KeyValue { + String key; + std::optional<HTTPHeaderName> keyAsHTTPHeaderName; + String value; + }; + + const KeyValue* get() const + { + ASSERT(*this != m_table.end()); + return &m_keyValue; + } + const KeyValue& operator*() const { return *get(); } + const KeyValue* operator->() const { return get(); } - // Gets a copy of the data suitable for passing to another thread. - PassOwnPtr<CrossThreadHTTPHeaderMapData> copyData() const; + HTTPHeaderMapConstIterator& operator++() + { + if (m_commonHeadersIt != m_table.m_commonHeaders.end()) { + if (updateKeyValue(++m_commonHeadersIt)) + return *this; + } else + ++m_uncommonHeadersIt; - void adopt(PassOwnPtr<CrossThreadHTTPHeaderMapData>); - - String get(const AtomicString& name) const; + updateKeyValue(m_uncommonHeadersIt); + return *this; + } - AddResult add(const AtomicString& name, const String& value); + bool operator!=(const HTTPHeaderMapConstIterator& other) const { return !(*this == other); } + bool operator==(const HTTPHeaderMapConstIterator& other) const + { + return m_commonHeadersIt == other.m_commonHeadersIt && m_uncommonHeadersIt == other.m_uncommonHeadersIt; + } - // Alternate accessors that are faster than converting the char* to AtomicString first. - bool contains(const char*) const; - String get(const char*) const; - AddResult add(const char* name, const String& value); - + private: + bool updateKeyValue(CommonHeadersHashMap::const_iterator it) + { + if (it == m_table.commonHeaders().end()) + return false; + m_keyValue.key = httpHeaderNameString(it->key).toStringWithoutCopying(); + m_keyValue.keyAsHTTPHeaderName = it->key; + m_keyValue.value = it->value; + return true; + } + bool updateKeyValue(UncommonHeadersHashMap::const_iterator it) + { + if (it == m_table.uncommonHeaders().end()) + return false; + m_keyValue.key = it->key; + m_keyValue.keyAsHTTPHeaderName = std::nullopt; + m_keyValue.value = it->value; + return true; + } + + const HTTPHeaderMap& m_table; + CommonHeadersHashMap::const_iterator m_commonHeadersIt; + UncommonHeadersHashMap::const_iterator m_uncommonHeadersIt; + KeyValue m_keyValue; }; + typedef HTTPHeaderMapConstIterator const_iterator; -} // namespace WebCore + WEBCORE_EXPORT HTTPHeaderMap(); + + // Gets a copy of the data suitable for passing to another thread. + HTTPHeaderMap isolatedCopy() const; + + bool isEmpty() const { return m_commonHeaders.isEmpty() && m_uncommonHeaders.isEmpty(); } + int size() const { return m_commonHeaders.size() + m_uncommonHeaders.size(); } + + void clear() + { + m_commonHeaders.clear(); + m_uncommonHeaders.clear(); + } + + WEBCORE_EXPORT String get(const String& name) const; + WEBCORE_EXPORT void set(const String& name, const String& value); + WEBCORE_EXPORT void add(const String& name, const String& value); + WEBCORE_EXPORT bool contains(const String&) const; + bool remove(const String&); + +#if USE(CF) + void set(CFStringRef name, const String& value); +#ifdef __OBJC__ + void set(NSString *name, const String& value) { set((__bridge CFStringRef)name, value); } +#endif +#endif + + WEBCORE_EXPORT String get(HTTPHeaderName) const; + void set(HTTPHeaderName, const String& value); + void add(HTTPHeaderName, const String& value); + bool addIfNotPresent(HTTPHeaderName, const String&); + bool contains(HTTPHeaderName) const; + WEBCORE_EXPORT bool remove(HTTPHeaderName); + + // Instead of passing a string literal to any of these functions, just use a HTTPHeaderName instead. + template<size_t length> String get(const char (&)[length]) const = delete; + template<size_t length> void set(const char (&)[length], const String&) = delete; + template<size_t length> bool contains(const char (&)[length]) = delete; + template<size_t length> bool remove(const char (&)[length]) = delete; -#endif // HTTPHeaderMap_h + const CommonHeadersHashMap& commonHeaders() const { return m_commonHeaders; } + const UncommonHeadersHashMap& uncommonHeaders() const { return m_uncommonHeaders; } + CommonHeadersHashMap& commonHeaders() { return m_commonHeaders; } + UncommonHeadersHashMap& uncommonHeaders() { return m_uncommonHeaders; } + + const_iterator begin() const { return const_iterator(*this, m_commonHeaders.begin(), m_uncommonHeaders.begin()); } + const_iterator end() const { return const_iterator(*this, m_commonHeaders.end(), m_uncommonHeaders.end()); } + + friend bool operator==(const HTTPHeaderMap& a, const HTTPHeaderMap& b) + { + return a.m_commonHeaders == b.m_commonHeaders && a.m_uncommonHeaders == b.m_uncommonHeaders; + } + + friend bool operator!=(const HTTPHeaderMap& a, const HTTPHeaderMap& b) + { + return !(a == b); + } + + template <class Encoder> void encode(Encoder&) const; + template <class Decoder> static bool decode(Decoder&, HTTPHeaderMap&); + +private: + CommonHeadersHashMap m_commonHeaders; + UncommonHeadersHashMap m_uncommonHeaders; +}; + +template <class Encoder> +void HTTPHeaderMap::encode(Encoder& encoder) const +{ + encoder << static_cast<uint64_t>(m_commonHeaders.size()); + for (const auto& keyValuePair : m_commonHeaders) { + encoder.encodeEnum(keyValuePair.key); + encoder << keyValuePair.value; + } + + encoder << m_uncommonHeaders; +} + +template <class Decoder> +bool HTTPHeaderMap::decode(Decoder& decoder, HTTPHeaderMap& headerMap) +{ + uint64_t commonHeadersSize; + if (!decoder.decode(commonHeadersSize)) + return false; + for (size_t i = 0; i < commonHeadersSize; ++i) { + HTTPHeaderName name; + if (!decoder.decodeEnum(name)) + return false; + String value; + if (!decoder.decode(value)) + return false; + headerMap.m_commonHeaders.add(name, value); + } + + if (!decoder.decode(headerMap.m_uncommonHeaders)) + return false; + + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/HTTPHeaderNames.in b/Source/WebCore/platform/network/HTTPHeaderNames.in new file mode 100644 index 000000000..0699c9bcb --- /dev/null +++ b/Source/WebCore/platform/network/HTTPHeaderNames.in @@ -0,0 +1,112 @@ +// +// Copyright (C) 2014 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. +// + +Accept +Accept-Charset +Accept-Language +Accept-Encoding +Accept-Ranges +Access-Control-Allow-Credentials +Access-Control-Allow-Headers +Access-Control-Allow-Methods +Access-Control-Allow-Origin +Access-Control-Expose-Headers +Access-Control-Max-Age +Access-Control-Request-Headers +Access-Control-Request-Method +Age +Alternate-Protocol +Authorization +Cache-Control +Connection +Content-Disposition +Content-Encoding +Content-Language +Content-Length +Content-Security-Policy +Content-Security-Policy-Report-Only +Content-Type +Content-Transfer-Encoding +Content-Range +Cookie +Cookie2 +Date +DNT +Default-Style +ETag +Expect +Expires +Host +If-Match +If-Modified-Since +If-None-Match +If-Range +If-Unmodified-Since +Keep-Alive +Last-Event-ID +Last-Modified +Location +Link +Origin +Ping-From +Ping-To +Purpose +Pragma +Range +Referer +Refresh +Sec-WebSocket-Accept +Sec-WebSocket-Extensions +Sec-WebSocket-Key +Sec-WebSocket-Protocol +Sec-WebSocket-Version +Server +Set-Cookie +Set-Cookie2 +TE +Timing-Allow-Origin +Trailer +Transfer-Encoding +Upgrade +Upgrade-Insecure-Requests +User-Agent +Vary +Via +X-Check-Cacheable +X-Content-Type-Options +X-DNS-Prefetch-Control +X-Frame-Options +X-Powered-By +X-WebKit-CSP +X-WebKit-CSP-Report-Only +X-XSS-Protection + +// These headers are specific to GStreamer. +Icy-Genre +Icy-MetaInt +Icy-Metadata +Icy-Name +Icy-Title +Icy-URL diff --git a/Source/WebCore/platform/network/HTTPHeaderValues.cpp b/Source/WebCore/platform/network/HTTPHeaderValues.cpp new file mode 100644 index 000000000..72df220a0 --- /dev/null +++ b/Source/WebCore/platform/network/HTTPHeaderValues.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 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 "HTTPHeaderValues.h" + +#include <wtf/NeverDestroyed.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +namespace HTTPHeaderValues { + +const String& textPlainContentType() +{ + static NeverDestroyed<const String> contentType(ASCIILiteral("text/plain;charset=UTF-8")); + return contentType; +} + +const String& formURLEncodedContentType() +{ + static NeverDestroyed<const String> contentType(ASCIILiteral("application/x-www-form-urlencoded;charset=UTF-8")); + return contentType; +} + +const String& noCache() +{ + static NeverDestroyed<const String> value(ASCIILiteral("no-cache")); + return value; +} + +const String& maxAge0() +{ + static NeverDestroyed<const String> value(ASCIILiteral("max-age=0")); + return value; +} + +} + +} diff --git a/Source/WebCore/platform/network/HTTPHeaderValues.h b/Source/WebCore/platform/network/HTTPHeaderValues.h new file mode 100644 index 000000000..9d85f961b --- /dev/null +++ b/Source/WebCore/platform/network/HTTPHeaderValues.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#include <wtf/Forward.h> + +namespace WebCore { + +namespace HTTPHeaderValues { + +const String& textPlainContentType(); +const String& formURLEncodedContentType(); +const String& noCache(); +const String& maxAge0(); +} + +} diff --git a/Source/WebCore/platform/network/HTTPParsers.cpp b/Source/WebCore/platform/network/HTTPParsers.cpp index 54b85d7f1..158e2d9b5 100644 --- a/Source/WebCore/platform/network/HTTPParsers.cpp +++ b/Source/WebCore/platform/network/HTTPParsers.cpp @@ -14,7 +14,7 @@ * 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 + * 3. Neither the name of Apple 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. * @@ -33,8 +33,10 @@ #include "config.h" #include "HTTPParsers.h" -#include "ContentSecurityPolicy.h" +#include "HTTPHeaderNames.h" +#include "Language.h" #include <wtf/DateMath.h> +#include <wtf/NeverDestroyed.h> #include <wtf/text/CString.h> #include <wtf/text/StringBuilder.h> #include <wtf/text/WTFString.h> @@ -46,17 +48,12 @@ namespace WebCore { // true if there is more to parse, after incrementing pos past whitespace. // Note: Might return pos == str.length() -static inline bool skipWhiteSpace(const String& str, unsigned& pos, bool fromHttpEquivMeta) +static inline bool skipWhiteSpace(const String& str, unsigned& pos) { unsigned len = str.length(); - if (fromHttpEquivMeta) { - while (pos < len && str[pos] <= ' ') - ++pos; - } else { - while (pos < len && (str[pos] == '\t' || str[pos] == ' ')) - ++pos; - } + while (pos < len && (str[pos] == '\t' || str[pos] == ' ')) + ++pos; return pos < len; } @@ -85,7 +82,7 @@ static inline bool skipToken(const String& str, unsigned& pos, const char* token // True if the expected equals sign is seen and there is more to follow. static inline bool skipEquals(const String& str, unsigned &pos) { - return skipWhiteSpace(str, pos, false) && str[pos++] == '=' && skipWhiteSpace(str, pos, false); + return skipWhiteSpace(str, pos) && str[pos++] == '=' && skipWhiteSpace(str, pos); } // True if a value present, incrementing pos to next space or semicolon, if any. @@ -102,21 +99,82 @@ static inline bool skipValue(const String& str, unsigned& pos) return pos != start; } -bool isValidHTTPHeaderValue(const String& name) +// See RFC 7230, Section 3.1.2. +bool isValidReasonPhrase(const String& value) +{ + for (unsigned i = 0; i < value.length(); ++i) { + UChar c = value[i]; + if (c == 0x7F || c > 0xFF || (c < 0x20 && c != '\t')) + return false; + } + return true; +} + +// See RFC 7230, Section 3.2.3. +bool isValidHTTPHeaderValue(const String& value) { - // FIXME: This should really match name against - // field-value in section 4.2 of RFC 2616. + UChar c = value[0]; + if (c == ' ' || c == '\t') + return false; + c = value[value.length() - 1]; + if (c == ' ' || c == '\t') + return false; + for (unsigned i = 0; i < value.length(); ++i) { + c = value[i]; + if (c == 0x7F || c > 0xFF || (c < 0x20 && c != '\t')) + return false; + } + return true; +} + +// See RFC 7230, Section 3.2.6. +static bool isDelimiterCharacter(const UChar c) +{ + // DQUOTE and "(),/:;<=>?@[\]{}" + return (c == '"' || c == '(' || c == ')' || c == ',' || c == '/' || c == ':' || c == ';' + || c == '<' || c == '=' || c == '>' || c == '?' || c == '@' || c == '[' || c == '\\' + || c == ']' || c == '{' || c == '}'); +} + +// See RFC 7231, Section 5.3.2. +bool isValidAcceptHeaderValue(const String& value) +{ + for (unsigned i = 0; i < value.length(); ++i) { + UChar c = value[i]; + // First check for alphanumeric for performance reasons then whitelist four delimiter characters. + if (isASCIIAlphanumeric(c) || c == ',' || c == '/' || c == ';' || c == '=') + continue; + if (isDelimiterCharacter(c)) + return false; + } + + return true; +} - return !name.contains('\r') && !name.contains('\n'); +// See RFC 7231, Section 5.3.5 and 3.1.3.2. +bool isValidLanguageHeaderValue(const String& value) +{ + for (unsigned i = 0; i < value.length(); ++i) { + UChar c = value[i]; + if (isASCIIAlphanumeric(c) || c == ' ' || c == '*' || c == ',' || c == '-' || c == '.' || c == ';' || c == '=') + continue; + return false; + } + + // FIXME: Validate further by splitting into language tags and optional quality + // values (q=) and then check each language tag. + // Language tags https://tools.ietf.org/html/rfc7231#section-3.1.3.1 + // Language tag syntax https://tools.ietf.org/html/bcp47#section-2.1 + return true; } -// See RFC 2616, Section 2.2. -bool isValidHTTPToken(const String& characters) +// See RFC 7230, Section 3.2.6. +bool isValidHTTPToken(const String& value) { - if (characters.isEmpty()) + if (value.isEmpty()) return false; - for (unsigned i = 0; i < characters.length(); ++i) { - UChar c = characters[i]; + auto valueStringView = StringView(value); + for (UChar c : valueStringView.codeUnits()) { if (c <= 0x20 || c >= 0x7F || c == '(' || c == ')' || c == '<' || c == '>' || c == '@' || c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' @@ -136,42 +194,12 @@ static String trimInputSample(const char* p, size_t length) return s; } -ContentDispositionType contentDispositionType(const String& contentDisposition) -{ - if (contentDisposition.isEmpty()) - return ContentDispositionNone; - - Vector<String> parameters; - contentDisposition.split(';', parameters); - - String dispositionType = parameters[0]; - dispositionType.stripWhiteSpace(); - - if (equalIgnoringCase(dispositionType, "inline")) - return ContentDispositionInline; - - // Some broken sites just send bogus headers like - // - // Content-Disposition: ; filename="file" - // Content-Disposition: filename="file" - // Content-Disposition: name="file" - // - // without a disposition token... screen those out. - if (!isValidHTTPToken(dispositionType)) - 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) +bool parseHTTPRefresh(const String& refresh, double& delay, String& url) { unsigned len = refresh.length(); unsigned pos = 0; - if (!skipWhiteSpace(refresh, pos, fromHttpEquivMeta)) + if (!skipWhiteSpace(refresh, pos)) return false; while (pos != len && refresh[pos] != ',' && refresh[pos] != ';') @@ -189,14 +217,14 @@ bool parseHTTPRefresh(const String& refresh, bool fromHttpEquivMeta, double& del return false; ++pos; - skipWhiteSpace(refresh, pos, fromHttpEquivMeta); + skipWhiteSpace(refresh, pos); unsigned urlStartPos = pos; if (refresh.find("url", urlStartPos, false) == urlStartPos) { urlStartPos += 3; - skipWhiteSpace(refresh, urlStartPos, fromHttpEquivMeta); + skipWhiteSpace(refresh, urlStartPos); if (refresh[urlStartPos] == '=') { ++urlStartPos; - skipWhiteSpace(refresh, urlStartPos, fromHttpEquivMeta); + skipWhiteSpace(refresh, urlStartPos); } else urlStartPos = pos; // e.g. "Refresh: 0; url.html" } @@ -214,7 +242,7 @@ bool parseHTTPRefresh(const String& refresh, bool fromHttpEquivMeta, double& del // 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 + // If we looped over the entire alleged URL string back to the opening quote, just use everything // after the opening quote instead. if (urlEndPos == urlStartPos) urlEndPos = len; @@ -225,9 +253,14 @@ bool parseHTTPRefresh(const String& refresh, bool fromHttpEquivMeta, double& del } } -double parseDate(const String& value) +std::optional<std::chrono::system_clock::time_point> parseHTTPDate(const String& value) { - return parseDateFromNullTerminatedCharacters(value.utf8().data()); + double dateInMillisecondsSinceEpoch = parseDateFromNullTerminatedCharacters(value.utf8().data()); + if (!std::isfinite(dateInMillisecondsSinceEpoch)) + return { }; + // This assumes system_clock epoch equals Unix epoch which is true for all implementations but unspecified. + // FIXME: The parsing function should be switched to std::chrono too. + return std::chrono::system_clock::time_point(std::chrono::milliseconds(static_cast<long long>(dateInMillisecondsSinceEpoch))); } // FIXME: This function doesn't comply with RFC 6266. @@ -349,46 +382,46 @@ void findCharsetInMediaType(const String& mediaType, unsigned int& charsetPos, u } } -ContentSecurityPolicy::ReflectedXSSDisposition parseXSSProtectionHeader(const String& header, String& failureReason, unsigned& failurePosition, String& reportURL) +XSSProtectionDisposition parseXSSProtectionHeader(const String& header, String& failureReason, unsigned& failurePosition, String& reportURL) { - DEFINE_STATIC_LOCAL(String, failureReasonInvalidToggle, (ASCIILiteral("expected 0 or 1"))); - DEFINE_STATIC_LOCAL(String, failureReasonInvalidSeparator, (ASCIILiteral("expected semicolon"))); - DEFINE_STATIC_LOCAL(String, failureReasonInvalidEquals, (ASCIILiteral("expected equals sign"))); - DEFINE_STATIC_LOCAL(String, failureReasonInvalidMode, (ASCIILiteral("invalid mode directive"))); - DEFINE_STATIC_LOCAL(String, failureReasonInvalidReport, (ASCIILiteral("invalid report directive"))); - DEFINE_STATIC_LOCAL(String, failureReasonDuplicateMode, (ASCIILiteral("duplicate mode directive"))); - DEFINE_STATIC_LOCAL(String, failureReasonDuplicateReport, (ASCIILiteral("duplicate report directive"))); - DEFINE_STATIC_LOCAL(String, failureReasonInvalidDirective, (ASCIILiteral("unrecognized directive"))); + static NeverDestroyed<String> failureReasonInvalidToggle(ASCIILiteral("expected 0 or 1")); + static NeverDestroyed<String> failureReasonInvalidSeparator(ASCIILiteral("expected semicolon")); + static NeverDestroyed<String> failureReasonInvalidEquals(ASCIILiteral("expected equals sign")); + static NeverDestroyed<String> failureReasonInvalidMode(ASCIILiteral("invalid mode directive")); + static NeverDestroyed<String> failureReasonInvalidReport(ASCIILiteral("invalid report directive")); + static NeverDestroyed<String> failureReasonDuplicateMode(ASCIILiteral("duplicate mode directive")); + static NeverDestroyed<String> failureReasonDuplicateReport(ASCIILiteral("duplicate report directive")); + static NeverDestroyed<String> failureReasonInvalidDirective(ASCIILiteral("unrecognized directive")); unsigned pos = 0; - if (!skipWhiteSpace(header, pos, false)) - return ContentSecurityPolicy::ReflectedXSSUnset; + if (!skipWhiteSpace(header, pos)) + return XSSProtectionDisposition::Enabled; if (header[pos] == '0') - return ContentSecurityPolicy::AllowReflectedXSS; + return XSSProtectionDisposition::Disabled; if (header[pos++] != '1') { failureReason = failureReasonInvalidToggle; - return ContentSecurityPolicy::ReflectedXSSInvalid; + return XSSProtectionDisposition::Invalid; } - ContentSecurityPolicy::ReflectedXSSDisposition result = ContentSecurityPolicy::FilterReflectedXSS; + XSSProtectionDisposition result = XSSProtectionDisposition::Enabled; bool modeDirectiveSeen = false; bool reportDirectiveSeen = false; while (1) { // At end of previous directive: consume whitespace, semicolon, and whitespace. - if (!skipWhiteSpace(header, pos, false)) + if (!skipWhiteSpace(header, pos)) return result; if (header[pos++] != ';') { failureReason = failureReasonInvalidSeparator; failurePosition = pos; - return ContentSecurityPolicy::ReflectedXSSInvalid; + return XSSProtectionDisposition::Invalid; } - if (!skipWhiteSpace(header, pos, false)) + if (!skipWhiteSpace(header, pos)) return result; // At start of next directive. @@ -396,44 +429,44 @@ ContentSecurityPolicy::ReflectedXSSDisposition parseXSSProtectionHeader(const St if (modeDirectiveSeen) { failureReason = failureReasonDuplicateMode; failurePosition = pos; - return ContentSecurityPolicy::ReflectedXSSInvalid; + return XSSProtectionDisposition::Invalid; } modeDirectiveSeen = true; if (!skipEquals(header, pos)) { failureReason = failureReasonInvalidEquals; failurePosition = pos; - return ContentSecurityPolicy::ReflectedXSSInvalid; + return XSSProtectionDisposition::Invalid; } if (!skipToken(header, pos, "block")) { failureReason = failureReasonInvalidMode; failurePosition = pos; - return ContentSecurityPolicy::ReflectedXSSInvalid; + return XSSProtectionDisposition::Invalid; } - result = ContentSecurityPolicy::BlockReflectedXSS; + result = XSSProtectionDisposition::BlockEnabled; } else if (skipToken(header, pos, "report")) { if (reportDirectiveSeen) { failureReason = failureReasonDuplicateReport; failurePosition = pos; - return ContentSecurityPolicy::ReflectedXSSInvalid; + return XSSProtectionDisposition::Invalid; } reportDirectiveSeen = true; if (!skipEquals(header, pos)) { failureReason = failureReasonInvalidEquals; failurePosition = pos; - return ContentSecurityPolicy::ReflectedXSSInvalid; + return XSSProtectionDisposition::Invalid; } size_t startPos = pos; if (!skipValue(header, pos)) { failureReason = failureReasonInvalidReport; failurePosition = pos; - return ContentSecurityPolicy::ReflectedXSSInvalid; + return XSSProtectionDisposition::Invalid; } reportURL = header.substring(startPos, pos - startPos); failurePosition = startPos; // If later semantic check deems unacceptable. } else { failureReason = failureReasonInvalidDirective; failurePosition = pos; - return ContentSecurityPolicy::ReflectedXSSInvalid; + return XSSProtectionDisposition::Invalid; } } } @@ -441,18 +474,19 @@ ContentSecurityPolicy::ReflectedXSSDisposition parseXSSProtectionHeader(const St #if ENABLE(NOSNIFF) ContentTypeOptionsDisposition parseContentTypeOptionsHeader(const String& header) { - if (header.stripWhiteSpace().lower() == "nosniff") + if (equalLettersIgnoringASCIICase(header.stripWhiteSpace(), "nosniff")) return ContentTypeOptionsNosniff; return ContentTypeOptionsNone; } #endif -String extractReasonPhraseFromHTTPStatusLine(const String& statusLine) +AtomicString extractReasonPhraseFromHTTPStatusLine(const String& statusLine) { - size_t spacePos = statusLine.find(' '); + StringView view = statusLine; + size_t spacePos = view.find(' '); // Remove status code from the status line. - spacePos = statusLine.find(' ', spacePos + 1); - return statusLine.substring(spacePos + 1); + spacePos = view.find(' ', spacePos + 1); + return view.substring(spacePos + 1).toAtomicString(); } XFrameOptionsDisposition parseXFrameOptionsHeader(const String& header) @@ -468,11 +502,11 @@ XFrameOptionsDisposition parseXFrameOptionsHeader(const String& header) for (size_t i = 0; i < headers.size(); i++) { String currentHeader = headers[i].stripWhiteSpace(); XFrameOptionsDisposition currentValue = XFrameOptionsNone; - if (equalIgnoringCase(currentHeader, "deny")) + if (equalLettersIgnoringASCIICase(currentHeader, "deny")) currentValue = XFrameOptionsDeny; - else if (equalIgnoringCase(currentHeader, "sameorigin")) + else if (equalLettersIgnoringASCIICase(currentHeader, "sameorigin")) currentValue = XFrameOptionsSameOrigin; - else if (equalIgnoringCase(currentHeader, "allowall")) + else if (equalLettersIgnoringASCIICase(currentHeader, "allowall")) currentValue = XFrameOptionsAllowAll; else currentValue = XFrameOptionsInvalid; @@ -568,20 +602,20 @@ size_t parseHTTPRequestLine(const char* data, size_t length, String& failureReas // Haven't finished header line. if (consumedLength == length) { - failureReason = "Incomplete Request Line"; + failureReason = ASCIILiteral("Incomplete Request Line"); return 0; } // RequestLine does not contain 3 parts. if (!space1 || !space2) { - failureReason = "Request Line does not appear to contain: <Method> <Url> <HTTPVersion>."; + failureReason = ASCIILiteral("Request Line does not appear to contain: <Method> <Url> <HTTPVersion>."); return 0; } // The line must end with "\r\n". const char* end = p + 1; if (*(end - 2) != '\r') { - failureReason = "Request line does not end with CRLF"; + failureReason = ASCIILiteral("Request line does not end with CRLF"); return 0; } @@ -605,14 +639,48 @@ size_t parseHTTPRequestLine(const char* data, size_t length, String& failureReas return end - data; } -size_t parseHTTPHeader(const char* start, size_t length, String& failureReason, AtomicString& nameStr, String& valueStr, bool strict) +static inline bool isValidHeaderNameCharacter(const char* character) +{ + // https://tools.ietf.org/html/rfc7230#section-3.2 + // A header name should only contain one or more of + // alphanumeric or ! # $ % & ' * + - . ^ _ ` | ~ + if (isASCIIAlphanumeric(*character)) + return true; + switch (*character) { + case '!': + case '#': + case '$': + case '%': + case '&': + case '\'': + case '*': + case '+': + case '-': + case '.': + case '^': + case '_': + case '`': + case '|': + case '~': + return true; + default: + return false; + } +} + +size_t parseHTTPHeader(const char* start, size_t length, String& failureReason, StringView& nameStr, String& valueStr, bool strict) { const char* p = start; const char* end = start + length; Vector<char> name; Vector<char> value; - nameStr = AtomicString(); + + bool foundFirstNameChar = false; + const char* namePtr = nullptr; + size_t nameSize = 0; + + nameStr = StringView(); valueStr = String(); for (; p < end; p++) { @@ -621,18 +689,29 @@ size_t parseHTTPHeader(const char* start, size_t length, String& failureReason, if (name.isEmpty()) { if (p + 1 < end && *(p + 1) == '\n') return (p + 2) - start; - failureReason = "CR doesn't follow LF at " + trimInputSample(p, end - p); + failureReason = makeString("CR doesn't follow LF in header name at ", trimInputSample(p, end - p)); return 0; } - failureReason = "Unexpected CR in name at " + trimInputSample(name.data(), name.size()); + failureReason = makeString("Unexpected CR in header name at ", trimInputSample(name.data(), name.size())); return 0; case '\n': - failureReason = "Unexpected LF in name at " + trimInputSample(name.data(), name.size()); + failureReason = makeString("Unexpected LF in header name at ", trimInputSample(name.data(), name.size())); return 0; case ':': break; default: + if (!isValidHeaderNameCharacter(p)) { + if (name.size() < 1) + failureReason = "Unexpected start character in header name"; + else + failureReason = makeString("Unexpected character in header name at ", trimInputSample(name.data(), name.size())); + return 0; + } name.append(*p); + if (!foundFirstNameChar) { + namePtr = p; + foundFirstNameChar = true; + } continue; } if (*p == ':') { @@ -641,6 +720,9 @@ size_t parseHTTPHeader(const char* start, size_t length, String& failureReason, } } + nameSize = name.size(); + nameStr = StringView(reinterpret_cast<const LChar*>(namePtr), nameSize); + for (; p < end && *p == 0x20; p++) { } for (; p < end; p++) { @@ -649,7 +731,7 @@ size_t parseHTTPHeader(const char* start, size_t length, String& failureReason, break; case '\n': if (strict) { - failureReason = "Unexpected LF in value at " + trimInputSample(value.data(), value.size()); + failureReason = makeString("Unexpected LF in header value at ", trimInputSample(value.data(), value.size())); return 0; } break; @@ -662,17 +744,12 @@ size_t parseHTTPHeader(const char* start, size_t length, String& failureReason, } } if (p >= end || (strict && *p != '\n')) { - failureReason = "CR doesn't follow LF after value at " + trimInputSample(p, end - p); + failureReason = makeString("CR doesn't follow LF after header value at ", trimInputSample(p, end - p)); return 0; } - nameStr = AtomicString::fromUTF8(name.data(), name.size()); valueStr = String::fromUTF8(value.data(), value.size()); - if (nameStr.isNull()) { - failureReason = "Invalid UTF-8 sequence in header name"; - return 0; - } if (valueStr.isNull()) { - failureReason = "Invalid UTF-8 sequence in header value"; + failureReason = ASCIILiteral("Invalid UTF-8 sequence in header value"); return 0; } return p - start; @@ -686,4 +763,111 @@ size_t parseHTTPRequestBody(const char* data, size_t length, Vector<unsigned cha return length; } +void parseAccessControlExposeHeadersAllowList(const String& headerValue, HTTPHeaderSet& headerSet) +{ + Vector<String> headers; + headerValue.split(',', false, headers); + for (auto& header : headers) { + String strippedHeader = header.stripWhiteSpace(); + if (!strippedHeader.isEmpty()) + headerSet.add(strippedHeader); + } +} + +// Implementation of https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name +bool isForbiddenHeaderName(const String& name) +{ + HTTPHeaderName headerName; + if (findHTTPHeaderName(name, headerName)) { + switch (headerName) { + case HTTPHeaderName::AcceptCharset: + case HTTPHeaderName::AcceptEncoding: + case HTTPHeaderName::AccessControlRequestHeaders: + case HTTPHeaderName::AccessControlRequestMethod: + case HTTPHeaderName::Connection: + case HTTPHeaderName::ContentLength: + case HTTPHeaderName::Cookie: + case HTTPHeaderName::Cookie2: + case HTTPHeaderName::Date: + case HTTPHeaderName::DNT: + case HTTPHeaderName::Expect: + case HTTPHeaderName::Host: + case HTTPHeaderName::KeepAlive: + case HTTPHeaderName::Origin: + case HTTPHeaderName::Referer: + case HTTPHeaderName::TE: + case HTTPHeaderName::Trailer: + case HTTPHeaderName::TransferEncoding: + case HTTPHeaderName::Upgrade: + case HTTPHeaderName::Via: + return true; + default: + break; + } + } + return startsWithLettersIgnoringASCIICase(name, "sec-") || startsWithLettersIgnoringASCIICase(name, "proxy-"); +} + +bool isForbiddenResponseHeaderName(const String& name) +{ + return equalLettersIgnoringASCIICase(name, "set-cookie") || equalLettersIgnoringASCIICase(name, "set-cookie2"); +} + +bool isSimpleHeader(const String& name, const String& value) +{ + HTTPHeaderName headerName; + if (!findHTTPHeaderName(name, headerName)) + return false; + return isCrossOriginSafeRequestHeader(headerName, value); +} + +bool isCrossOriginSafeHeader(HTTPHeaderName name, const HTTPHeaderSet& accessControlExposeHeaderSet) +{ + switch (name) { + case HTTPHeaderName::CacheControl: + case HTTPHeaderName::ContentLanguage: + case HTTPHeaderName::ContentType: + case HTTPHeaderName::Expires: + case HTTPHeaderName::LastModified: + case HTTPHeaderName::Pragma: + case HTTPHeaderName::Accept: + return true; + case HTTPHeaderName::SetCookie: + case HTTPHeaderName::SetCookie2: + return false; + default: + break; + } + return accessControlExposeHeaderSet.contains(httpHeaderNameString(name).toStringWithoutCopying()); +} + +bool isCrossOriginSafeHeader(const String& name, const HTTPHeaderSet& accessControlExposeHeaderSet) +{ +#ifndef ASSERT_DISABLED + HTTPHeaderName headerName; + ASSERT(!findHTTPHeaderName(name, headerName)); +#endif + return accessControlExposeHeaderSet.contains(name); +} + +// Implements https://fetch.spec.whatwg.org/#cors-safelisted-request-header +bool isCrossOriginSafeRequestHeader(HTTPHeaderName name, const String& value) +{ + switch (name) { + case HTTPHeaderName::Accept: + return isValidAcceptHeaderValue(value); + case HTTPHeaderName::AcceptLanguage: + case HTTPHeaderName::ContentLanguage: + return isValidLanguageHeaderValue(value); + case HTTPHeaderName::ContentType: { + // Preflight is required for MIME types that can not be sent via form submission. + String mimeType = extractMIMETypeFromMediaType(value); + return equalLettersIgnoringASCIICase(mimeType, "application/x-www-form-urlencoded") || equalLettersIgnoringASCIICase(mimeType, "multipart/form-data") || equalLettersIgnoringASCIICase(mimeType, "text/plain"); + } + default: + // FIXME: Should we also make safe other headers (DPR, Downlink, Save-Data...)? That would require validating their values. + return false; + } +} + } diff --git a/Source/WebCore/platform/network/HTTPParsers.h b/Source/WebCore/platform/network/HTTPParsers.h index 2c570f3c4..0c04240ae 100644 --- a/Source/WebCore/platform/network/HTTPParsers.h +++ b/Source/WebCore/platform/network/HTTPParsers.h @@ -12,7 +12,7 @@ * 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 + * 3. Neither the name of Apple 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. * @@ -31,20 +31,24 @@ #ifndef HTTPParsers_h #define HTTPParsers_h -#include "ContentSecurityPolicy.h" #include <wtf/Forward.h> +#include <wtf/HashSet.h> +#include <wtf/Optional.h> #include <wtf/Vector.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> namespace WebCore { -class HTTPHeaderMap; -class ResourceResponseBase; +typedef HashSet<String, ASCIICaseInsensitiveHash> HTTPHeaderSet; -enum ContentDispositionType { - ContentDispositionNone, - ContentDispositionInline, - ContentDispositionAttachment, - ContentDispositionOther +enum class HTTPHeaderName; + +enum class XSSProtectionDisposition { + Invalid, + Disabled, + Enabled, + BlockEnabled, }; #if ENABLE(NOSNIFF) @@ -63,21 +67,23 @@ enum XFrameOptionsDisposition { XFrameOptionsConflict }; -ContentDispositionType contentDispositionType(const String&); +bool isValidReasonPhrase(const String&); bool isValidHTTPHeaderValue(const String&); +bool isValidAcceptHeaderValue(const String&); +bool isValidLanguageHeaderValue(const String&); bool isValidHTTPToken(const String&); -bool parseHTTPRefresh(const String& refresh, bool fromHttpEquivMeta, double& delay, String& url); -double parseDate(const String&); -String filenameFromHTTPContentDisposition(const String&); +bool parseHTTPRefresh(const String& refresh, double& delay, String& url); +std::optional<std::chrono::system_clock::time_point> parseHTTPDate(const String&); +String filenameFromHTTPContentDisposition(const String&); String extractMIMETypeFromMediaType(const String&); -String extractCharsetFromMediaType(const String&); +String extractCharsetFromMediaType(const String&); void findCharsetInMediaType(const String& mediaType, unsigned int& charsetPos, unsigned int& charsetLen, unsigned int start = 0); -ContentSecurityPolicy::ReflectedXSSDisposition parseXSSProtectionHeader(const String& header, String& failureReason, unsigned& failurePosition, String& reportURL); -String extractReasonPhraseFromHTTPStatusLine(const String&); +XSSProtectionDisposition parseXSSProtectionHeader(const String& header, String& failureReason, unsigned& failurePosition, String& reportURL); +AtomicString extractReasonPhraseFromHTTPStatusLine(const String&); XFrameOptionsDisposition parseXFrameOptionsHeader(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); +WEBCORE_EXPORT bool parseRange(const String&, long long& rangeOffset, long long& rangeEnd, long long& rangeSuffixLength); #if ENABLE(NOSNIFF) ContentTypeOptionsDisposition parseContentTypeOptionsHeader(const String& header); @@ -86,9 +92,30 @@ ContentTypeOptionsDisposition parseContentTypeOptionsHeader(const String& header // Parsing Complete HTTP Messages. enum HTTPVersion { Unknown, HTTP_1_0, HTTP_1_1 }; size_t parseHTTPRequestLine(const char* data, size_t length, String& failureReason, String& method, String& url, HTTPVersion&); -size_t parseHTTPHeader(const char* data, size_t length, String& failureReason, AtomicString& nameStr, String& valueStr, bool strict = true); +size_t parseHTTPHeader(const char* data, size_t length, String& failureReason, StringView& nameStr, String& valueStr, bool strict = true); size_t parseHTTPRequestBody(const char* data, size_t length, Vector<unsigned char>& body); +void parseAccessControlExposeHeadersAllowList(const String& headerValue, HTTPHeaderSet&); + +// HTTP Header routine as per https://fetch.spec.whatwg.org/#terminology-headers +bool isForbiddenHeaderName(const String&); +bool isForbiddenResponseHeaderName(const String&); +bool isSimpleHeader(const String& name, const String& value); +bool isCrossOriginSafeHeader(HTTPHeaderName, const HTTPHeaderSet&); +bool isCrossOriginSafeHeader(const String&, const HTTPHeaderSet&); +bool isCrossOriginSafeRequestHeader(HTTPHeaderName, const String&); + +inline bool isHTTPSpace(UChar character) +{ + return character <= ' ' && (character == ' ' || character == '\n' || character == '\t' || character == '\r'); +} + +// Strip leading and trailing whitespace as defined in https://fetch.spec.whatwg.org/#concept-header-value-normalize. +inline String stripLeadingAndTrailingHTTPSpaces(const String& string) +{ + return string.stripWhiteSpace(isHTTPSpace); +} + } #endif diff --git a/Source/WebCore/platform/network/HTTPStatusCodes.h b/Source/WebCore/platform/network/HTTPStatusCodes.h new file mode 100644 index 000000000..c1ac8dc55 --- /dev/null +++ b/Source/WebCore/platform/network/HTTPStatusCodes.h @@ -0,0 +1,27 @@ +/* + Copyright (C) 2011 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. +*/ + +namespace WebCore { + +enum HTTPStatusCodes { + HTTPNoContent = 204, + HTTPResetContent = 205 +}; + +} diff --git a/Source/WebCore/platform/network/MIMEHeader.cpp b/Source/WebCore/platform/network/MIMEHeader.cpp index 048abf807..267b597b1 100644 --- a/Source/WebCore/platform/network/MIMEHeader.cpp +++ b/Source/WebCore/platform/network/MIMEHeader.cpp @@ -43,13 +43,13 @@ namespace WebCore { typedef HashMap<String, String> KeyValueMap; -static KeyValueMap retrieveKeyValuePairs(WebCore::SharedBufferChunkReader* buffer) +static KeyValueMap retrieveKeyValuePairs(WebCore::SharedBufferChunkReader& buffer) { KeyValueMap keyValuePairs; String line; String key; StringBuilder value; - while (!(line = buffer->nextChunkAsUTF8StringWithLatin1Fallback()).isNull()) { + while (!(line = buffer.nextChunkAsUTF8StringWithLatin1Fallback()).isNull()) { if (line.isEmpty()) break; // Empty line means end of key/value section. if (line[0] == '\t') { @@ -70,7 +70,7 @@ static KeyValueMap retrieveKeyValuePairs(WebCore::SharedBufferChunkReader* buffe // This is not a key value pair, ignore. continue; } - key = line.substring(0, semiColonIndex).lower().stripWhiteSpace(); + key = line.substring(0, semiColonIndex).convertToASCIILowercase().stripWhiteSpace(); value.append(line.substring(semiColonIndex + 1)); } // Store the last property if there is one. @@ -79,9 +79,9 @@ static KeyValueMap retrieveKeyValuePairs(WebCore::SharedBufferChunkReader* buffe return keyValuePairs; } -PassRefPtr<MIMEHeader> MIMEHeader::parseHeader(SharedBufferChunkReader* buffer) +RefPtr<MIMEHeader> MIMEHeader::parseHeader(SharedBufferChunkReader& buffer) { - RefPtr<MIMEHeader> mimeHeader = adoptRef(new MIMEHeader); + auto mimeHeader = adoptRef(*new MIMEHeader); KeyValueMap keyValuePairs = retrieveKeyValuePairs(buffer); KeyValueMap::iterator mimeParametersIterator = keyValuePairs.find("content-type"); if (mimeParametersIterator != keyValuePairs.end()) { @@ -94,11 +94,10 @@ PassRefPtr<MIMEHeader> MIMEHeader::parseHeader(SharedBufferChunkReader* buffer) mimeHeader->m_endOfPartBoundary = parsedContentType.parameterValueForName("boundary"); if (mimeHeader->m_endOfPartBoundary.isNull()) { LOG_ERROR("No boundary found in multipart MIME header."); - return 0; + return nullptr; } - mimeHeader->m_endOfPartBoundary.insert("--", 0); - mimeHeader->m_endOfDocumentBoundary = mimeHeader->m_endOfPartBoundary; - mimeHeader->m_endOfDocumentBoundary.append("--"); + mimeHeader->m_endOfPartBoundary = "--" + mimeHeader->m_endOfPartBoundary; + mimeHeader->m_endOfDocumentBoundary = mimeHeader->m_endOfPartBoundary + "--"; } } @@ -110,19 +109,19 @@ PassRefPtr<MIMEHeader> MIMEHeader::parseHeader(SharedBufferChunkReader* buffer) if (mimeParametersIterator != keyValuePairs.end()) mimeHeader->m_contentLocation = mimeParametersIterator->value; - return mimeHeader.release(); + return WTFMove(mimeHeader); } MIMEHeader::Encoding MIMEHeader::parseContentTransferEncoding(const String& text) { - String encoding = text.stripWhiteSpace().lower(); - if (encoding == "base64") + String encoding = text.stripWhiteSpace(); + if (equalLettersIgnoringASCIICase(encoding, "base64")) return Base64; - if (encoding == "quoted-printable") + if (equalLettersIgnoringASCIICase(encoding, "quoted-printable")) return QuotedPrintable; - if (encoding == "7bit") + if (equalLettersIgnoringASCIICase(encoding, "7bit")) return SevenBit; - if (encoding == "binary") + if (equalLettersIgnoringASCIICase(encoding, "binary")) return Binary; LOG_ERROR("Unknown encoding '%s' found in MIME header.", text.ascii().data()); return Unknown; diff --git a/Source/WebCore/platform/network/MIMEHeader.h b/Source/WebCore/platform/network/MIMEHeader.h index d22d55122..4df3e28fb 100644 --- a/Source/WebCore/platform/network/MIMEHeader.h +++ b/Source/WebCore/platform/network/MIMEHeader.h @@ -31,7 +31,6 @@ #ifndef MIMEHeader_h #define MIMEHeader_h -#include <wtf/HashMap.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> #include <wtf/text/WTFString.h> @@ -51,7 +50,7 @@ public: Unknown }; - static PassRefPtr<MIMEHeader> parseHeader(SharedBufferChunkReader* crLFLineReader); + static RefPtr<MIMEHeader> parseHeader(SharedBufferChunkReader& crLFLineReader); bool isMultipart() const { return m_contentType.startsWith("multipart/"); } diff --git a/Source/WebCore/platform/network/NetworkLoadTiming.h b/Source/WebCore/platform/network/NetworkLoadTiming.h new file mode 100644 index 000000000..4432124bc --- /dev/null +++ b/Source/WebCore/platform/network/NetworkLoadTiming.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * Copyright (C) 2014 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. + */ + +#pragma once + +#if PLATFORM(COCOA) +OBJC_CLASS NSDictionary; +#endif + +namespace WebCore { + +class NetworkLoadTiming { +public: + NetworkLoadTiming() + : domainLookupStart(-1) + , domainLookupEnd(-1) + , connectStart(-1) + , connectEnd(-1) + , requestStart(0) + , responseStart(0) + , secureConnectionStart(-1) + { + } + + NetworkLoadTiming(const NetworkLoadTiming& other) + : domainLookupStart(other.domainLookupStart) + , domainLookupEnd(other.domainLookupEnd) + , connectStart(other.connectStart) + , connectEnd(other.connectEnd) + , requestStart(other.requestStart) + , responseStart(other.responseStart) + , secureConnectionStart(other.secureConnectionStart) + { + } + + NetworkLoadTiming& operator=(const NetworkLoadTiming& other) + { + domainLookupStart = other.domainLookupStart; + domainLookupEnd = other.domainLookupEnd; + connectStart = other.connectStart; + connectEnd = other.connectEnd; + requestStart = other.requestStart; + responseStart = other.responseStart; + secureConnectionStart = other.secureConnectionStart; + return *this; + } + + NetworkLoadTiming isolatedCopy() const + { + // There are currently no members that need isolated copies, so we can use the copy constructor. + return *this; + } + + bool operator==(const NetworkLoadTiming& other) const + { + return domainLookupStart == other.domainLookupStart + && domainLookupEnd == other.domainLookupEnd + && connectStart == other.connectStart + && connectEnd == other.connectEnd + && requestStart == other.requestStart + && responseStart == other.responseStart + && secureConnectionStart == other.secureConnectionStart; + } + + bool operator!=(const NetworkLoadTiming& other) const + { + return !(*this == other); + } + + template<class Encoder> void encode(Encoder&) const; + template<class Decoder> static bool decode(Decoder&, NetworkLoadTiming&); + + // These are millisecond deltas from the start time. + double domainLookupStart; + double domainLookupEnd; + double connectStart; + double connectEnd; + double requestStart; + double responseStart; + double secureConnectionStart; +}; + +#if PLATFORM(COCOA) +WEBCORE_EXPORT void copyTimingData(NSDictionary *timingData, NetworkLoadTiming&); +#endif + +#if PLATFORM(COCOA) && !HAVE(TIMINGDATAOPTIONS) +WEBCORE_EXPORT void setCollectsTimingData(); +#endif + +template<class Encoder> +void NetworkLoadTiming::encode(Encoder& encoder) const +{ + encoder << domainLookupStart; + encoder << domainLookupEnd; + encoder << connectStart; + encoder << connectEnd; + encoder << requestStart; + encoder << responseStart; + encoder << secureConnectionStart; +} + +template<class Decoder> +bool NetworkLoadTiming::decode(Decoder& decoder, NetworkLoadTiming& timing) +{ + return decoder.decode(timing.domainLookupStart) + && decoder.decode(timing.domainLookupEnd) + && decoder.decode(timing.connectStart) + && decoder.decode(timing.connectEnd) + && decoder.decode(timing.requestStart) + && decoder.decode(timing.responseStart) + && decoder.decode(timing.secureConnectionStart); +} + +} diff --git a/Source/WebCore/platform/network/NetworkStateNotifier.cpp b/Source/WebCore/platform/network/NetworkStateNotifier.cpp index 31fab8f07..5574a3583 100644 --- a/Source/WebCore/platform/network/NetworkStateNotifier.cpp +++ b/Source/WebCore/platform/network/NetworkStateNotifier.cpp @@ -26,32 +26,41 @@ #include "config.h" #include "NetworkStateNotifier.h" +#if PLATFORM(IOS) +#include "Settings.h" +#endif + #include <mutex> #include <wtf/Assertions.h> -#include <wtf/StdLibExtras.h> +#include <wtf/NeverDestroyed.h> namespace WebCore { NetworkStateNotifier& networkStateNotifier() { static std::once_flag onceFlag; - static NetworkStateNotifier* networkStateNotifier; + static LazyNeverDestroyed<NetworkStateNotifier> networkStateNotifier; std::call_once(onceFlag, []{ - networkStateNotifier = std::make_unique<NetworkStateNotifier>().release(); + networkStateNotifier.construct(); }); - return *networkStateNotifier; + return networkStateNotifier; } void NetworkStateNotifier::addNetworkStateChangeListener(std::function<void (bool)> listener) { ASSERT(listener); +#if PLATFORM(IOS) + if (Settings::shouldOptOutOfNetworkStateObservation()) + return; + registerObserverIfNecessary(); +#endif - m_listeners.append(std::move(listener)); + m_listeners.append(WTFMove(listener)); } -void NetworkStateNotifier::notifyNetworkStateChange() +void NetworkStateNotifier::notifyNetworkStateChange() const { for (const auto& listener : m_listeners) listener(m_isOnLine); diff --git a/Source/WebCore/platform/network/NetworkStateNotifier.h b/Source/WebCore/platform/network/NetworkStateNotifier.h index 17639969b..5282ec066 100644 --- a/Source/WebCore/platform/network/NetworkStateNotifier.h +++ b/Source/WebCore/platform/network/NetworkStateNotifier.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2014 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,7 +31,7 @@ #include <wtf/Noncopyable.h> #include <wtf/Vector.h> -#if PLATFORM(MAC) && !PLATFORM(IOS) +#if PLATFORM(MAC) #include <wtf/RetainPtr.h> #include "Timer.h" @@ -43,10 +43,10 @@ typedef const struct __SCDynamicStore * SCDynamicStoreRef; #include <windows.h> -#elif PLATFORM(EFL) +#elif PLATFORM(IOS) -typedef struct _Ecore_Fd_Handler Ecore_Fd_Handler; -typedef unsigned char Eina_Bool; +#include <wtf/RetainPtr.h> +OBJC_CLASS WebNetworkStateObserver; #endif @@ -56,51 +56,49 @@ class NetworkStateNotifier { WTF_MAKE_NONCOPYABLE(NetworkStateNotifier); WTF_MAKE_FAST_ALLOCATED; public: NetworkStateNotifier(); -#if PLATFORM(EFL) +#if PLATFORM(IOS) ~NetworkStateNotifier(); #endif void addNetworkStateChangeListener(std::function<void (bool isOnLine)>); - bool onLine() const { return m_isOnLine; } - -#if PLATFORM(IOS) - void setIsOnLine(bool); -#endif + bool onLine() const; private: +#if !PLATFORM(IOS) bool m_isOnLine; +#endif Vector<std::function<void (bool)>> m_listeners; - void notifyNetworkStateChange(); + void notifyNetworkStateChange() const; void updateState(); -#if PLATFORM(MAC) && !PLATFORM(IOS) - void networkStateChangeTimerFired(Timer<NetworkStateNotifier>&); +#if PLATFORM(MAC) + void networkStateChangeTimerFired(); static void dynamicStoreCallback(SCDynamicStoreRef, CFArrayRef changedKeys, void *info); RetainPtr<SCDynamicStoreRef> m_store; - Timer<NetworkStateNotifier> m_networkStateChangeTimer; + Timer 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(EFL) - void networkInterfaceChanged(); - static Eina_Bool readSocketCallback(void* userData, Ecore_Fd_Handler*); +#elif PLATFORM(IOS) + void registerObserverIfNecessary() const; + friend void setOnLine(const NetworkStateNotifier*, bool); - int m_netlinkSocket; - Ecore_Fd_Handler* m_fdHandler; + mutable bool m_isOnLine; + mutable bool m_isOnLineInitialized; + mutable RetainPtr<WebNetworkStateObserver> m_observer; #endif }; -#if !PLATFORM(MAC) && !PLATFORM(WIN) && !PLATFORM(EFL) +#if !PLATFORM(COCOA) && !PLATFORM(WIN) inline NetworkStateNotifier::NetworkStateNotifier() : m_isOnLine(true) @@ -111,8 +109,15 @@ inline void NetworkStateNotifier::updateState() { } #endif -NetworkStateNotifier& networkStateNotifier(); +#if !PLATFORM(IOS) +inline bool NetworkStateNotifier::onLine() const +{ + return m_isOnLine; +} +#endif -}; +WEBCORE_EXPORT NetworkStateNotifier& networkStateNotifier(); + +} // namespace WebCore #endif // NetworkStateNotifier_h diff --git a/Source/WebCore/platform/network/ResourceLoadTiming.cpp b/Source/WebCore/platform/network/NetworkStorageSession.cpp index e33439581..985b4cdd0 100644 --- a/Source/WebCore/platform/network/ResourceLoadTiming.cpp +++ b/Source/WebCore/platform/network/NetworkStorageSession.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Google, Inc. All Rights Reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,13 +24,36 @@ */ #include "config.h" -#include "ResourceLoadTiming.h" +#include "NetworkStorageSession.h" + +#include "SessionID.h" +#include <wtf/NeverDestroyed.h> namespace WebCore { -double ResourceLoadTiming::convertResourceLoadTimeToMonotonicTime(int deltaMilliseconds) const +HashMap<SessionID, std::unique_ptr<NetworkStorageSession>>& NetworkStorageSession::globalSessionMap() +{ + static NeverDestroyed<HashMap<SessionID, std::unique_ptr<NetworkStorageSession>>> map; + return map; +} + +NetworkStorageSession* NetworkStorageSession::storageSession(SessionID sessionID) +{ + if (sessionID == SessionID::defaultSessionID()) + return &defaultStorageSession(); + return globalSessionMap().get(sessionID); +} + +void NetworkStorageSession::destroySession(SessionID sessionID) +{ + globalSessionMap().remove(sessionID); +} + +void NetworkStorageSession::forEach(std::function<void(const WebCore::NetworkStorageSession&)> functor) { - return requestTime + deltaMilliseconds / 1000.0; + functor(defaultStorageSession()); + for (auto& storageSession : globalSessionMap().values()) + functor(*storageSession); } } diff --git a/Source/WebCore/platform/network/NetworkStorageSession.h b/Source/WebCore/platform/network/NetworkStorageSession.h index bb6fb7fd6..aedb08a7f 100644 --- a/Source/WebCore/platform/network/NetworkStorageSession.h +++ b/Source/WebCore/platform/network/NetworkStorageSession.h @@ -23,71 +23,92 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef NetworkStorageSession_h -#define NetworkStorageSession_h +#pragma once -#include <wtf/RetainPtr.h> +#include "CredentialStorage.h" +#include "SessionID.h" #include <wtf/text/WTFString.h> -#if PLATFORM(MAC) || USE(CFNETWORK) -typedef const struct __CFURLStorageSession* CFURLStorageSessionRef; -typedef struct OpaqueCFHTTPCookieStorage* CFHTTPCookieStorageRef; +#if PLATFORM(COCOA) || USE(CFURLCONNECTION) +#include "CFNetworkSPI.h" +#include <wtf/RetainPtr.h> +#endif + +#if USE(SOUP) +#include <wtf/Function.h> +#include <wtf/glib/GRefPtr.h> +typedef struct _SoupCookieJar SoupCookieJar; #endif namespace WebCore { class NetworkingContext; +class ResourceRequest; class SoupNetworkSession; class NetworkStorageSession { WTF_MAKE_NONCOPYABLE(NetworkStorageSession); WTF_MAKE_FAST_ALLOCATED; public: - static NetworkStorageSession& defaultStorageSession(); - static std::unique_ptr<NetworkStorageSession> createPrivateBrowsingSession(const String& identifierBase = String()); + WEBCORE_EXPORT static NetworkStorageSession& defaultStorageSession(); + WEBCORE_EXPORT static NetworkStorageSession* storageSession(SessionID); + WEBCORE_EXPORT static void ensurePrivateBrowsingSession(SessionID, const String& identifierBase = String()); + WEBCORE_EXPORT static void destroySession(SessionID); + WEBCORE_EXPORT static void forEach(std::function<void(const WebCore::NetworkStorageSession&)>); - static void switchToNewTestingSession(); + WEBCORE_EXPORT static void switchToNewTestingSession(); -#if PLATFORM(MAC) || USE(CFNETWORK) || USE(SOUP) - bool isPrivateBrowsingSession() const { return m_isPrivate; } -#endif + SessionID sessionID() const { return m_sessionID; } + CredentialStorage& credentialStorage() { return m_credentialStorage; } + +#if PLATFORM(COCOA) || USE(CFURLCONNECTION) + NetworkStorageSession(SessionID, RetainPtr<CFURLStorageSessionRef>); -#if PLATFORM(MAC) || USE(CFNETWORK) - NetworkStorageSession(RetainPtr<CFURLStorageSessionRef>); // May be null, in which case a Foundation default should be used. CFURLStorageSessionRef platformSession() { return m_platformSession.get(); } - RetainPtr<CFHTTPCookieStorageRef> cookieStorage() const; + WEBCORE_EXPORT RetainPtr<CFHTTPCookieStorageRef> cookieStorage() const; + WEBCORE_EXPORT static void setCookieStoragePartitioningEnabled(bool); #elif USE(SOUP) - NetworkStorageSession(std::unique_ptr<SoupNetworkSession>); + NetworkStorageSession(SessionID, std::unique_ptr<SoupNetworkSession>&&); ~NetworkStorageSession(); - SoupNetworkSession& soupNetworkSession() const; - void setSoupNetworkSession(std::unique_ptr<SoupNetworkSession>); + + SoupNetworkSession* soupNetworkSession() const { return m_session.get(); }; + SoupNetworkSession& getOrCreateSoupNetworkSession() const; + SoupCookieJar* cookieStorage() const; + void setCookieStorage(SoupCookieJar*); + void setCookieObserverHandler(Function<void ()>&&); + void getCredentialFromPersistentStorage(const ProtectionSpace&, Function<void (Credential&&)> completionHandler); + void saveCredentialToPersistentStorage(const ProtectionSpace&, const Credential&); #else - NetworkStorageSession(NetworkingContext*); + NetworkStorageSession(SessionID, NetworkingContext*); ~NetworkStorageSession(); + NetworkingContext* context() const; #endif private: -#if PLATFORM(MAC) || USE(CFNETWORK) - NetworkStorageSession(); + static HashMap<SessionID, std::unique_ptr<NetworkStorageSession>>& globalSessionMap(); + SessionID m_sessionID; + +#if PLATFORM(COCOA) || USE(CFURLCONNECTION) RetainPtr<CFURLStorageSessionRef> m_platformSession; #elif USE(SOUP) - std::unique_ptr<SoupNetworkSession> m_session; + static void cookiesDidChange(NetworkStorageSession*); + + mutable std::unique_ptr<SoupNetworkSession> m_session; + GRefPtr<SoupCookieJar> m_cookieStorage; + Function<void ()> m_cookieObserverHandler; +#if USE(LIBSECRET) + Function<void (Credential&&)> m_persisentStorageCompletionHandler; + GRefPtr<GCancellable> m_persisentStorageCancellable; +#endif #else RefPtr<NetworkingContext> m_context; #endif -#if PLATFORM(MAC) || USE(CFNETWORK) || USE(SOUP) - bool m_isPrivate; -#endif + CredentialStorage m_credentialStorage; }; -#if PLATFORM(WIN) && USE(CFNETWORK) -// Needed for WebKit1 API only. -void overrideCookieStorage(CFHTTPCookieStorageRef); -CFHTTPCookieStorageRef overridenCookieStorage(); -#endif +WEBCORE_EXPORT String cookieStoragePartition(const ResourceRequest&); +String cookieStoragePartition(const URL& firstPartyForCookies, const URL& resource); } - -#endif // NetworkStorageSession_h diff --git a/Source/WebCore/platform/network/NetworkStorageSessionStub.cpp b/Source/WebCore/platform/network/NetworkStorageSessionStub.cpp new file mode 100644 index 000000000..fd98432e6 --- /dev/null +++ b/Source/WebCore/platform/network/NetworkStorageSessionStub.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 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" + +#if !USE(SOUP) + +#include "NetworkStorageSession.h" + +#include "NetworkingContext.h" +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +NetworkStorageSession::NetworkStorageSession(SessionID sessionID, NetworkingContext* context) + : m_sessionID(sessionID) + , m_context(context) +{ +} + +NetworkStorageSession::~NetworkStorageSession() +{ +} + +NetworkingContext* NetworkStorageSession::context() const +{ + return m_context.get(); +} + +void NetworkStorageSession::ensurePrivateBrowsingSession(SessionID sessionID, const String&) +{ + ASSERT_NOT_REACHED(); +} + +static std::unique_ptr<NetworkStorageSession>& defaultSession() +{ + static NeverDestroyed<std::unique_ptr<NetworkStorageSession>> session; + return session; +} + +NetworkStorageSession& NetworkStorageSession::defaultStorageSession() +{ + if (!defaultSession()) + defaultSession() = std::make_unique<NetworkStorageSession>(SessionID::defaultSessionID(), nullptr); + return *defaultSession(); +} + +void NetworkStorageSession::switchToNewTestingSession() +{ +} + +} + +#endif diff --git a/Source/WebCore/platform/network/NetworkingContext.h b/Source/WebCore/platform/network/NetworkingContext.h index 3080a7e9d..95e2d5e7c 100644 --- a/Source/WebCore/platform/network/NetworkingContext.h +++ b/Source/WebCore/platform/network/NetworkingContext.h @@ -17,18 +17,17 @@ Boston, MA 02110-1301, USA. */ -#ifndef NetworkingContext_h -#define NetworkingContext_h +#pragma once -#include "NetworkStorageSession.h" #include <wtf/RefCounted.h> #include <wtf/RetainPtr.h> +#include <wtf/text/WTFString.h> -#if PLATFORM(MAC) +#if PLATFORM(COCOA) #include <wtf/SchedulePair.h> #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) OBJC_CLASS NSOperationQueue; #endif @@ -38,6 +37,7 @@ typedef struct _SoupSession SoupSession; namespace WebCore { +class NetworkStorageSession; class ResourceError; class ResourceRequest; @@ -49,21 +49,18 @@ public: virtual bool shouldClearReferrerOnHTTPSToHTTPRedirect() const = 0; -#if PLATFORM(MAC) - virtual bool needsSiteSpecificQuirks() const = 0; +#if PLATFORM(COCOA) virtual bool localFileContentSniffingEnabled() const = 0; // FIXME: Reconcile with ResourceHandle::forceContentSniffing(). virtual SchedulePairHashSet* scheduledRunLoopPairs() const { return 0; } virtual RetainPtr<CFDataRef> sourceApplicationAuditData() const = 0; virtual ResourceError blockedError(const ResourceRequest&) const = 0; #endif -#if PLATFORM(MAC) || USE(CFNETWORK) || USE(SOUP) + virtual String sourceApplicationIdentifier() const { return emptyString(); } + virtual NetworkStorageSession& storageSession() const = 0; -#endif #if PLATFORM(WIN) - virtual String userAgent() const = 0; - virtual String referrer() const = 0; virtual ResourceError blockedError(const ResourceRequest&) const = 0; #endif @@ -72,5 +69,3 @@ protected: }; } - -#endif // NetworkingContext_h diff --git a/Source/WebCore/platform/network/ParsedContentRange.cpp b/Source/WebCore/platform/network/ParsedContentRange.cpp new file mode 100644 index 000000000..00b90c485 --- /dev/null +++ b/Source/WebCore/platform/network/ParsedContentRange.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2016 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 "ParsedContentRange.h" + +#include <wtf/StdLibExtras.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +static bool areContentRangeValuesValid(int64_t firstBytePosition, int64_t lastBytePosition, int64_t instanceLength) +{ + // From <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html> + // 14.16 Content-Range + // A byte-content-range-spec with a byte-range-resp-spec whose last- byte-pos value is less than its first-byte-pos value, + // or whose instance-length value is less than or equal to its last-byte-pos value, is invalid. + if (firstBytePosition < 0) + return false; + + if (lastBytePosition < firstBytePosition) + return false; + + if (instanceLength == ParsedContentRange::UnknownLength) + return true; + + return lastBytePosition < instanceLength; +} + +static bool parseContentRange(const String& headerValue, int64_t& firstBytePosition, int64_t& lastBytePosition, int64_t& instanceLength) +{ + // From <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html> + // 14.16 Content-Range + // + // Content-Range = "Content-Range" ":" content-range-spec + // content-range-spec = byte-content-range-spec + // byte-content-range-spec = bytes-unit SP + // byte-range-resp-spec "/" + // ( instance-length | "*" ) + // byte-range-resp-spec = (first-byte-pos "-" last-byte-pos) + // | "*" + // instance-length = 1*DIGIT + + static const char* prefix = "bytes "; + static const size_t prefixLength = 6; + + if (!headerValue.startsWith(prefix)) + return false; + + size_t byteSeparatorTokenLoc = headerValue.find('-', prefixLength); + if (byteSeparatorTokenLoc == notFound) + return false; + + size_t instanceLengthSeparatorToken = headerValue.find('/', byteSeparatorTokenLoc + 1); + if (instanceLengthSeparatorToken == notFound) + return false; + + bool isOk = true; + String firstByteString = headerValue.substring(prefixLength, byteSeparatorTokenLoc - prefixLength); + if (!firstByteString.isAllSpecialCharacters<isASCIIDigit>()) + return false; + + firstBytePosition = firstByteString.toInt64Strict(&isOk); + if (!isOk) + return false; + + String lastByteString = headerValue.substring(byteSeparatorTokenLoc + 1, instanceLengthSeparatorToken - (byteSeparatorTokenLoc + 1)); + if (!lastByteString.isAllSpecialCharacters<isASCIIDigit>()) + return false; + + lastBytePosition = lastByteString.toInt64Strict(&isOk); + if (!isOk) + return false; + + String instanceString = headerValue.substring(instanceLengthSeparatorToken + 1); + if (instanceString == "*") + instanceLength = ParsedContentRange::UnknownLength; + else { + if (!instanceString.isAllSpecialCharacters<isASCIIDigit>()) + return false; + + instanceLength = instanceString.toInt64Strict(&isOk); + if (!isOk) + return false; + } + + return areContentRangeValuesValid(firstBytePosition, lastBytePosition, instanceLength); +} + +ParsedContentRange::ParsedContentRange(const String& headerValue) +{ + m_isValid = parseContentRange(headerValue, m_firstBytePosition, m_lastBytePosition, m_instanceLength); +} + +ParsedContentRange::ParsedContentRange(int64_t firstBytePosition, int64_t lastBytePosition, int64_t instanceLength) + : m_firstBytePosition(firstBytePosition) + , m_lastBytePosition(lastBytePosition) + , m_instanceLength(instanceLength) +{ + m_isValid = areContentRangeValuesValid(m_firstBytePosition, m_lastBytePosition, m_instanceLength); +} + +String ParsedContentRange::headerValue() const +{ + if (!m_isValid) + return String(); + if (m_instanceLength == UnknownLength) + return String::format("bytes %" PRId64 "-%" PRId64 "/*", m_firstBytePosition, m_lastBytePosition); + return String::format("bytes %" PRId64 "-%" PRId64 "/%" PRId64, m_firstBytePosition, m_lastBytePosition, m_instanceLength); +} + +} diff --git a/Source/WebCore/platform/network/soup/CookieJarSoup.h b/Source/WebCore/platform/network/ParsedContentRange.h index b949ef8fa..281b803aa 100644 --- a/Source/WebCore/platform/network/soup/CookieJarSoup.h +++ b/Source/WebCore/platform/network/ParsedContentRange.h @@ -1,6 +1,5 @@ /* - * Copyright (C) 2008 Xan Lopez <xan@gnome.org> - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -21,20 +20,39 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CookieJarSoup_h -#define CookieJarSoup_h +#ifndef ParsedContentRange_h +#define ParsedContentRange_h -#include <libsoup/soup.h> +#include <wtf/Forward.h> namespace WebCore { -SoupCookieJar* soupCookieJar(); -void setSoupCookieJar(SoupCookieJar*); +class ParsedContentRange { +public: + WEBCORE_EXPORT explicit ParsedContentRange(const String&); + ParsedContentRange() { } + WEBCORE_EXPORT ParsedContentRange(int64_t firstBytePosition, int64_t lastBytePosition, int64_t instanceLength); -SoupCookieJar* createPrivateBrowsingCookieJar(); + bool isValid() const { return m_isValid; } + int64_t firstBytePosition() const { return m_firstBytePosition; } + int64_t lastBytePosition() const { return m_lastBytePosition; } + int64_t instanceLength() const { return m_instanceLength; } + + WEBCORE_EXPORT String headerValue() const; + + enum { UnknownLength = std::numeric_limits<int64_t>::max() }; + +private: + template<typename T> static bool isPositive(T); + + bool m_isValid { false }; + int64_t m_firstBytePosition { 0 }; + int64_t m_lastBytePosition { 0 }; + int64_t m_instanceLength { UnknownLength }; +}; } diff --git a/Source/WebCore/platform/network/PingHandle.h b/Source/WebCore/platform/network/PingHandle.h new file mode 100644 index 000000000..cd9f2bf96 --- /dev/null +++ b/Source/WebCore/platform/network/PingHandle.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "Timer.h" + +namespace WebCore { + +// This class triggers asynchronous loads independent of the networking context staying alive (i.e., auditing pingbacks). +// The object just needs to live long enough to ensure the message was actually sent. +// As soon as any callback is received from the ResourceHandle, this class will cancel the load and delete itself. + +class PingHandle final : private ResourceHandleClient { + WTF_MAKE_NONCOPYABLE(PingHandle); WTF_MAKE_FAST_ALLOCATED; +public: + enum class UsesAsyncCallbacks { + Yes, + No, + }; + + PingHandle(NetworkingContext* networkingContext, const ResourceRequest& request, bool shouldUseCredentialStorage, UsesAsyncCallbacks useAsyncCallbacks, bool shouldFollowRedirects) + : m_timeoutTimer(*this, &PingHandle::timeoutTimerFired) + , m_shouldUseCredentialStorage(shouldUseCredentialStorage) + , m_shouldFollowRedirects(shouldFollowRedirects) + , m_usesAsyncCallbacks(useAsyncCallbacks) + { + m_handle = ResourceHandle::create(networkingContext, request, this, false, false); + + // If the server never responds, this object will hang around forever. + // Set a very generous timeout, just in case. + m_timeoutTimer.startOneShot(60000); + } + +private: + ResourceRequest willSendRequest(ResourceHandle*, ResourceRequest&& request, ResourceResponse&&) final + { + return m_shouldFollowRedirects ? request : ResourceRequest(); + } + void willSendRequestAsync(ResourceHandle* handle, ResourceRequest&& request, ResourceResponse&&) final + { + if (m_shouldFollowRedirects) { + handle->continueWillSendRequest(WTFMove(request)); + return; + } + delete this; + } + void didReceiveResponse(ResourceHandle*, ResourceResponse&&) final { delete this; } + void didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&&, int) final { delete this; }; + void didFinishLoading(ResourceHandle*, double) final { delete this; } + void didFail(ResourceHandle*, const ResourceError&) final { delete this; } + bool shouldUseCredentialStorage(ResourceHandle*) final { return m_shouldUseCredentialStorage; } + bool usesAsyncCallbacks() final { return m_usesAsyncCallbacks == UsesAsyncCallbacks::Yes; } + void timeoutTimerFired() { delete this; } + + virtual ~PingHandle() + { + if (m_handle) { + ASSERT(m_handle->client() == this); + m_handle->clearClient(); + m_handle->cancel(); + } + } + + RefPtr<ResourceHandle> m_handle; + Timer m_timeoutTimer; + bool m_shouldUseCredentialStorage; + bool m_shouldFollowRedirects; + UsesAsyncCallbacks m_usesAsyncCallbacks; +}; + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/PlatformCookieJar.h b/Source/WebCore/platform/network/PlatformCookieJar.h index 5e122c194..d7270c7ca 100644 --- a/Source/WebCore/platform/network/PlatformCookieJar.h +++ b/Source/WebCore/platform/network/PlatformCookieJar.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2008, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2008, 2012, 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -26,6 +26,7 @@ #ifndef PlatformCookieJar_h #define PlatformCookieJar_h +#include <chrono> #include <wtf/Forward.h> #include <wtf/HashSet.h> #include <wtf/Vector.h> @@ -39,15 +40,17 @@ struct Cookie; // FIXME: These should probably be NetworkStorageSession member functions. -String cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const URL&); -void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, const String&); -bool cookiesEnabled(const NetworkStorageSession&, const URL& firstParty, const URL&); -String cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const URL&); -bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const URL&, Vector<Cookie>&); -void deleteCookie(const NetworkStorageSession&, const URL&, const String&); -void getHostnamesWithCookies(const NetworkStorageSession&, HashSet<String>& hostnames); -void deleteCookiesForHostname(const NetworkStorageSession&, const String& hostname); -void deleteAllCookies(const NetworkStorageSession&); +WEBCORE_EXPORT String cookiesForDOM(const NetworkStorageSession&, const URL& firstParty, const URL&); +WEBCORE_EXPORT void setCookiesFromDOM(const NetworkStorageSession&, const URL& firstParty, const URL&, const String&); +WEBCORE_EXPORT bool cookiesEnabled(const NetworkStorageSession&, const URL& firstParty, const URL&); +WEBCORE_EXPORT String cookieRequestHeaderFieldValue(const NetworkStorageSession&, const URL& firstParty, const URL&); +WEBCORE_EXPORT bool getRawCookies(const NetworkStorageSession&, const URL& firstParty, const URL&, Vector<Cookie>&); +WEBCORE_EXPORT void deleteCookie(const NetworkStorageSession&, const URL&, const String&); +WEBCORE_EXPORT void addCookie(const NetworkStorageSession&, const URL&, const Cookie&); +WEBCORE_EXPORT void getHostnamesWithCookies(const NetworkStorageSession&, HashSet<String>& hostnames); +WEBCORE_EXPORT void deleteCookiesForHostnames(const NetworkStorageSession&, const Vector<String>& cookieHostNames); +WEBCORE_EXPORT void deleteAllCookies(const NetworkStorageSession&); +WEBCORE_EXPORT void deleteAllCookiesModifiedSince(const NetworkStorageSession&, std::chrono::system_clock::time_point); } diff --git a/Source/WebCore/platform/network/ProtectionSpace.h b/Source/WebCore/platform/network/ProtectionSpace.h index 11369cdfa..769de805f 100644 --- a/Source/WebCore/platform/network/ProtectionSpace.h +++ b/Source/WebCore/platform/network/ProtectionSpace.h @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -23,67 +23,29 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ProtectionSpace_h -#define ProtectionSpace_h +#pragma once -#include <wtf/text/WTFString.h> +#if PLATFORM(COCOA) +#include "ProtectionSpaceCocoa.h" +#elif USE(CFURLCONNECTION) +#include "ProtectionSpaceCFNet.h" +#else -namespace WebCore { - -enum ProtectionSpaceServerType { - ProtectionSpaceServerHTTP = 1, - ProtectionSpaceServerHTTPS = 2, - ProtectionSpaceServerFTP = 3, - ProtectionSpaceServerFTPS = 4, - ProtectionSpaceProxyHTTP = 5, - ProtectionSpaceProxyHTTPS = 6, - ProtectionSpaceProxyFTP = 7, - ProtectionSpaceProxySOCKS = 8 -}; +#include "ProtectionSpaceBase.h" -enum ProtectionSpaceAuthenticationScheme { - ProtectionSpaceAuthenticationSchemeDefault = 1, - ProtectionSpaceAuthenticationSchemeHTTPBasic = 2, - ProtectionSpaceAuthenticationSchemeHTTPDigest = 3, - ProtectionSpaceAuthenticationSchemeHTMLForm = 4, - ProtectionSpaceAuthenticationSchemeNTLM = 5, - ProtectionSpaceAuthenticationSchemeNegotiate = 6, - ProtectionSpaceAuthenticationSchemeClientCertificateRequested = 7, - ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested = 8, - ProtectionSpaceAuthenticationSchemeUnknown = 100 -}; - -class ProtectionSpace { +namespace WebCore { +class ProtectionSpace : public ProtectionSpaceBase { 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; + ProtectionSpace() : ProtectionSpaceBase() { } + ProtectionSpace(const String& host, int port, ProtectionSpaceServerType serverType, const String& realm, ProtectionSpaceAuthenticationScheme authenticationScheme) + : ProtectionSpaceBase(host, port, serverType, realm, authenticationScheme) + { + } -private: - String m_host; - int m_port; - ProtectionSpaceServerType m_serverType; - String m_realm; - ProtectionSpaceAuthenticationScheme m_authenticationScheme; - bool m_isHashTableDeletedValue; + ProtectionSpace(WTF::HashTableDeletedValueType deletedValue) : ProtectionSpaceBase(deletedValue) { } }; -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 +#endif diff --git a/Source/WebCore/platform/network/ProtectionSpace.cpp b/Source/WebCore/platform/network/ProtectionSpaceBase.cpp index e926ad1e1..dfc74e522 100644 --- a/Source/WebCore/platform/network/ProtectionSpace.cpp +++ b/Source/WebCore/platform/network/ProtectionSpaceBase.cpp @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -23,23 +23,24 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" +#include "ProtectionSpaceBase.h" + #include "ProtectionSpace.h" -#if USE(CFNETWORK) && !PLATFORM(MAC) +#if USE(CFURLCONNECTION) && !PLATFORM(COCOA) #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("") +ProtectionSpaceBase::ProtectionSpaceBase() + : m_host(emptyString()) , m_port(0) , m_serverType(ProtectionSpaceServerHTTP) - , m_realm("") + , m_realm(emptyString()) , m_authenticationScheme(ProtectionSpaceAuthenticationSchemeDefault) , m_isHashTableDeletedValue(false) { @@ -47,32 +48,32 @@ ProtectionSpace::ProtectionSpace() // 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 : "") +ProtectionSpaceBase::ProtectionSpaceBase(const String& host, int port, ProtectionSpaceServerType serverType, const String& realm, ProtectionSpaceAuthenticationScheme authenticationScheme) + : m_host(host.length() ? host : emptyString()) , m_port(port) , m_serverType(serverType) - , m_realm(realm.length() ? realm : "") + , m_realm(realm.length() ? realm : emptyString()) , m_authenticationScheme(authenticationScheme) , m_isHashTableDeletedValue(false) { } -const String& ProtectionSpace::host() const -{ +const String& ProtectionSpaceBase::host() const +{ return m_host; } -int ProtectionSpace::port() const +int ProtectionSpaceBase::port() const { return m_port; } -ProtectionSpaceServerType ProtectionSpace::serverType() const +ProtectionSpaceServerType ProtectionSpaceBase::serverType() const { - return m_serverType; + return m_serverType; } -bool ProtectionSpace::isProxy() const +bool ProtectionSpaceBase::isProxy() const { return (m_serverType == ProtectionSpaceProxyHTTP || m_serverType == ProtectionSpaceProxyHTTPS || @@ -80,30 +81,45 @@ bool ProtectionSpace::isProxy() const m_serverType == ProtectionSpaceProxySOCKS); } -const String& ProtectionSpace::realm() const +const String& ProtectionSpaceBase::realm() const { return m_realm; } -ProtectionSpaceAuthenticationScheme ProtectionSpace::authenticationScheme() const +ProtectionSpaceAuthenticationScheme ProtectionSpaceBase::authenticationScheme() const { return m_authenticationScheme; } -bool ProtectionSpace::receivesCredentialSecurely() const +bool ProtectionSpaceBase::receivesCredentialSecurely() const { -#if USE(CFNETWORK) && !PLATFORM(MAC) - RetainPtr<CFURLProtectionSpaceRef> cfSpace = adoptCF(createCF(*this)); - return cfSpace && CFURLProtectionSpaceReceivesCredentialSecurely(cfSpace.get()); -#else - return (m_serverType == ProtectionSpaceServerHTTPS || + return (m_serverType == ProtectionSpaceServerHTTPS || m_serverType == ProtectionSpaceServerFTPS || m_serverType == ProtectionSpaceProxyHTTPS || m_authenticationScheme == ProtectionSpaceAuthenticationSchemeHTTPDigest); -#endif } -bool operator==(const ProtectionSpace& a, const ProtectionSpace& b) +bool ProtectionSpaceBase::isPasswordBased() const +{ + switch (m_authenticationScheme) { + case ProtectionSpaceAuthenticationSchemeDefault: + case ProtectionSpaceAuthenticationSchemeHTTPBasic: + case ProtectionSpaceAuthenticationSchemeHTTPDigest: + case ProtectionSpaceAuthenticationSchemeHTMLForm: + case ProtectionSpaceAuthenticationSchemeNTLM: + case ProtectionSpaceAuthenticationSchemeNegotiate: + return true; + case ProtectionSpaceAuthenticationSchemeClientCertificateRequested: + case ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested: + case ProtectionSpaceAuthenticationSchemeUnknown: + return false; + } + + return true; +} + + +bool ProtectionSpaceBase::compare(const ProtectionSpace& a, const ProtectionSpace& b) { if (a.host() != b.host()) return false; @@ -116,10 +132,8 @@ bool operator==(const ProtectionSpace& a, const ProtectionSpace& b) return false; if (a.authenticationScheme() != b.authenticationScheme()) return false; - - return true; -} + return ProtectionSpace::platformCompare(a, b); } - +} diff --git a/Source/WebCore/platform/network/ProtectionSpaceBase.h b/Source/WebCore/platform/network/ProtectionSpaceBase.h new file mode 100644 index 000000000..78e5200c2 --- /dev/null +++ b/Source/WebCore/platform/network/ProtectionSpaceBase.h @@ -0,0 +1,100 @@ +/* + * 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 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 ProtectionSpaceBase_h +#define ProtectionSpaceBase_h + +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class ProtectionSpace; + +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 ProtectionSpaceBase { + +public: + bool isHashTableDeletedValue() const { return m_isHashTableDeletedValue; } + + WEBCORE_EXPORT const String& host() const; + WEBCORE_EXPORT int port() const; + WEBCORE_EXPORT ProtectionSpaceServerType serverType() const; + WEBCORE_EXPORT bool isProxy() const; + WEBCORE_EXPORT const String& realm() const; + WEBCORE_EXPORT ProtectionSpaceAuthenticationScheme authenticationScheme() const; + + bool receivesCredentialSecurely() const; + WEBCORE_EXPORT bool isPasswordBased() const; + + bool encodingRequiresPlatformData() const { return false; } + + WEBCORE_EXPORT static bool compare(const ProtectionSpace&, const ProtectionSpace&); + +protected: + WEBCORE_EXPORT ProtectionSpaceBase(); + WEBCORE_EXPORT ProtectionSpaceBase(const String& host, int port, ProtectionSpaceServerType, const String& realm, ProtectionSpaceAuthenticationScheme); + + // Hash table deleted values, which are only constructed and never copied or destroyed. + ProtectionSpaceBase(WTF::HashTableDeletedValueType) : m_isHashTableDeletedValue(true) { } + + static bool platformCompare(const ProtectionSpace&, const ProtectionSpace&) { return true; } + +private: + String m_host; + int m_port; + ProtectionSpaceServerType m_serverType; + String m_realm; + ProtectionSpaceAuthenticationScheme m_authenticationScheme; + bool m_isHashTableDeletedValue; +}; + +inline bool operator==(const ProtectionSpace& a, const ProtectionSpace& b) { return ProtectionSpaceBase::compare(a, b); } +inline bool operator!=(const ProtectionSpace& a, const ProtectionSpace& b) { return !(a == b); } + +} // namespace WebCore + +#endif // ProtectionSpaceBase_h diff --git a/Source/WebCore/platform/network/ProtectionSpaceHash.h b/Source/WebCore/platform/network/ProtectionSpaceHash.h index 2d56b313b..4526cd4f8 100644 --- a/Source/WebCore/platform/network/ProtectionSpaceHash.h +++ b/Source/WebCore/platform/network/ProtectionSpaceHash.h @@ -37,8 +37,8 @@ struct ProtectionSpaceHash { unsigned hashCodes[5] = { protectionSpace.host().impl() ? protectionSpace.host().impl()->hash() : 0, static_cast<unsigned>(protectionSpace.port()), - protectionSpace.serverType(), - protectionSpace.authenticationScheme(), + static_cast<unsigned>(protectionSpace.serverType()), + static_cast<unsigned>(protectionSpace.authenticationScheme()), protectionSpace.realm().impl() ? protectionSpace.realm().impl()->hash() : 0 }; diff --git a/Source/WebCore/platform/network/ProxyServer.cpp b/Source/WebCore/platform/network/ProxyServer.cpp index 07c90cbf2..c6d67c21d 100644 --- a/Source/WebCore/platform/network/ProxyServer.cpp +++ b/Source/WebCore/platform/network/ProxyServer.cpp @@ -34,14 +34,14 @@ static void appendProxyServerString(StringBuilder& builder, const ProxyServer& p { switch (proxyServer.type()) { case ProxyServer::Direct: - builder.append("DIRECT"); + builder.appendLiteral("DIRECT"); return; case ProxyServer::HTTP: case ProxyServer::HTTPS: - builder.append("PROXY"); + builder.appendLiteral("PROXY"); break; case ProxyServer::SOCKS: - builder.append("SOCKS"); + builder.appendLiteral("SOCKS"); break; } @@ -63,7 +63,7 @@ String toString(const Vector<ProxyServer>& proxyServers) StringBuilder stringBuilder; for (size_t i = 0; i < proxyServers.size(); ++i) { if (i) - stringBuilder.append("; "); + stringBuilder.appendLiteral("; "); appendProxyServerString(stringBuilder, proxyServers[i]); } diff --git a/Source/WebCore/platform/network/ProxyServer.h b/Source/WebCore/platform/network/ProxyServer.h index 7c1914eba..76fdbba6a 100644 --- a/Source/WebCore/platform/network/ProxyServer.h +++ b/Source/WebCore/platform/network/ProxyServer.h @@ -68,11 +68,11 @@ private: }; // Return a vector of proxy servers for the given URL. -Vector<ProxyServer> proxyServersForURL(const URL&, const NetworkingContext*); +WEBCORE_EXPORT Vector<ProxyServer> proxyServersForURL(const URL&, 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>&); +WEBCORE_EXPORT String toString(const Vector<ProxyServer>&); } // namespace WebCore diff --git a/Source/WebCore/platform/network/ResourceErrorBase.cpp b/Source/WebCore/platform/network/ResourceErrorBase.cpp index 004a1992b..31ce763dd 100644 --- a/Source/WebCore/platform/network/ResourceErrorBase.cpp +++ b/Source/WebCore/platform/network/ResourceErrorBase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -31,7 +31,12 @@ namespace WebCore { const char* const errorDomainWebKitInternal = "WebKitInternal"; -ResourceError ResourceErrorBase::copy() const +inline const ResourceError& ResourceErrorBase::asResourceError() const +{ + return *static_cast<const ResourceError*>(this); +} + +ResourceError ResourceErrorBase::isolatedCopy() const { lazyInit(); @@ -40,10 +45,10 @@ ResourceError ResourceErrorBase::copy() const errorCopy.m_errorCode = m_errorCode; errorCopy.m_failingURL = m_failingURL.isolatedCopy(); errorCopy.m_localizedDescription = m_localizedDescription.isolatedCopy(); - errorCopy.m_isNull = m_isNull; - errorCopy.m_isCancellation = m_isCancellation; - errorCopy.m_isTimeout = m_isTimeout; - platformCopy(errorCopy); + errorCopy.m_type = m_type; + + errorCopy.doPlatformIsolatedCopy(asResourceError()); + return errorCopy; } @@ -52,12 +57,19 @@ void ResourceErrorBase::lazyInit() const const_cast<ResourceError*>(static_cast<const ResourceError*>(this))->platformLazyInit(); } +void ResourceErrorBase::setType(Type type) +{ + // setType should only be used to specialize the error type. + ASSERT(m_type == Type::General || m_type == Type::Null || (m_type == Type::Cancellation && type == Type::AccessControl)); + m_type = type; +} + bool ResourceErrorBase::compare(const ResourceError& a, const ResourceError& b) { if (a.isNull() && b.isNull()) return true; - if (a.isNull() || b.isNull()) + if (a.type() != b.type()) return false; if (a.domain() != b.domain()) @@ -72,13 +84,7 @@ bool ResourceErrorBase::compare(const ResourceError& a, const ResourceError& b) if (a.localizedDescription() != b.localizedDescription()) return false; - if (a.isCancellation() != b.isCancellation()) - return false; - - if (a.isTimeout() != b.isTimeout()) - return false; - - return platformCompare(a, b); + return ResourceError::platformCompare(a, b); } } // namespace WebCore diff --git a/Source/WebCore/platform/network/ResourceErrorBase.h b/Source/WebCore/platform/network/ResourceErrorBase.h index b4d777086..658c9caf2 100644 --- a/Source/WebCore/platform/network/ResourceErrorBase.h +++ b/Source/WebCore/platform/network/ResourceErrorBase.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. + * Copyright (C) 2016 Canon Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,22 +11,22 @@ * 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 + * 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 COMPUTER, INC. OR + * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceErrorBase_h -#define ResourceErrorBase_h +#pragma once +#include "URL.h" #include <wtf/text/WTFString.h> namespace WebCore { @@ -36,67 +37,63 @@ extern const char* const errorDomainWebKitInternal; // Used for errors that won' 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; } + ResourceError isolatedCopy() const; const String& domain() const { lazyInit(); return m_domain; } int errorCode() const { lazyInit(); return m_errorCode; } - const String& failingURL() const { lazyInit(); return m_failingURL; } + const URL& 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; } + enum class Type { + Null, + General, + AccessControl, + Cancellation, + Timeout + }; - void setIsTimeout(bool isTimeout) { m_isTimeout = isTimeout; } - bool isTimeout() const { return m_isTimeout; } + bool isNull() const { return m_type == Type::Null; } + bool isGeneral() const { return m_type == Type::General; } + bool isAccessControl() const { return m_type == Type::AccessControl; } + bool isCancellation() const { return m_type == Type::Cancellation; } + bool isTimeout() const { return m_type == Type::Timeout; } static bool compare(const ResourceError&, const ResourceError&); + void setType(Type); + Type type() const { return m_type; } + protected: - ResourceErrorBase() - : m_errorCode(0) - , m_isNull(true) - , m_isCancellation(false) - , m_isTimeout(false) - { - } + ResourceErrorBase(Type type) : m_type(type) { } - ResourceErrorBase(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) + ResourceErrorBase(const String& domain, int errorCode, const URL& failingURL, const String& localizedDescription, Type type) : m_domain(domain) , m_failingURL(failingURL) , m_localizedDescription(localizedDescription) , m_errorCode(errorCode) - , m_isNull(false) - , m_isCancellation(false) - , m_isTimeout(false) + , m_type(type) { } - void lazyInit() const; + WEBCORE_EXPORT 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 copy platform specific fields - void platformCopy(ResourceError&) const {} - // The ResourceError subclass may "shadow" this method to compare platform specific fields static bool platformCompare(const ResourceError&, const ResourceError&) { return true; } String m_domain; - String m_failingURL; + URL m_failingURL; String m_localizedDescription; - int m_errorCode; - bool m_isNull : 1; - bool m_isCancellation : 1; - bool m_isTimeout : 1; + int m_errorCode { 0 }; + Type m_type { Type::General }; + +private: + const ResourceError& asResourceError() const; }; 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 index 1a3994ec8..1fd0fd93a 100644 --- a/Source/WebCore/platform/network/ResourceHandle.cpp +++ b/Source/WebCore/platform/network/ResourceHandle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2004-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -34,6 +34,8 @@ #include "Timer.h" #include <algorithm> #include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/text/AtomicStringHash.h> #include <wtf/text/CString.h> namespace WebCore { @@ -48,7 +50,7 @@ static BuiltinResourceHandleConstructorMap& builtinResourceHandleConstructorMap( #else ASSERT(isMainThread()); #endif - DEFINE_STATIC_LOCAL(BuiltinResourceHandleConstructorMap, map, ()); + static NeverDestroyed<BuiltinResourceHandleConstructorMap> map; return map; } @@ -61,7 +63,7 @@ typedef HashMap<AtomicString, ResourceHandle::BuiltinSynchronousLoader> BuiltinR static BuiltinResourceHandleSynchronousLoaderMap& builtinResourceHandleSynchronousLoaderMap() { ASSERT(isMainThread()); - DEFINE_STATIC_LOCAL(BuiltinResourceHandleSynchronousLoaderMap, map, ()); + static NeverDestroyed<BuiltinResourceHandleSynchronousLoaderMap> map; return map; } @@ -71,7 +73,7 @@ void ResourceHandle::registerBuiltinSynchronousLoader(const AtomicString& protoc } ResourceHandle::ResourceHandle(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) - : d(adoptPtr(new ResourceHandleInternal(this, context, request, client, defersLoading, shouldContentSniff && shouldContentSniffURL(request.url())))) + : d(std::make_unique<ResourceHandleInternal>(this, context, request, client, defersLoading, shouldContentSniff && shouldContentSniffURL(request.url()))) { if (!request.url().isValid()) { scheduleFailure(InvalidURLFailure); @@ -84,22 +86,20 @@ ResourceHandle::ResourceHandle(NetworkingContext* context, const ResourceRequest } } -PassRefPtr<ResourceHandle> ResourceHandle::create(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) +RefPtr<ResourceHandle> ResourceHandle::create(NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) { - BuiltinResourceHandleConstructorMap::iterator protocolMapItem = builtinResourceHandleConstructorMap().find(request.url().protocol()); + if (auto constructor = builtinResourceHandleConstructorMap().get(request.url().protocol().toStringWithoutCopying())) + return constructor(request, client); - if (protocolMapItem != builtinResourceHandleConstructorMap().end()) - return protocolMapItem->value(request, client); - - RefPtr<ResourceHandle> newHandle(adoptRef(new ResourceHandle(context, request, client, defersLoading, shouldContentSniff))); + auto newHandle = adoptRef(*new ResourceHandle(context, request, client, defersLoading, shouldContentSniff)); if (newHandle->d->m_scheduledFailureType != NoFailure) - return newHandle.release(); + return WTFMove(newHandle); if (newHandle->start()) - return newHandle.release(); + return WTFMove(newHandle); - return 0; + return nullptr; } void ResourceHandle::scheduleFailure(FailureType type) @@ -108,7 +108,7 @@ void ResourceHandle::scheduleFailure(FailureType type) d->m_failureTimer.startOneShot(0); } -void ResourceHandle::failureTimerFired(Timer<ResourceHandle>&) +void ResourceHandle::failureTimerFired() { if (!client()) return; @@ -132,10 +132,8 @@ void ResourceHandle::failureTimerFired(Timer<ResourceHandle>&) void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) { - BuiltinResourceHandleSynchronousLoaderMap::iterator protocolMapItem = builtinResourceHandleSynchronousLoaderMap().find(request.url().protocol()); - - if (protocolMapItem != builtinResourceHandleSynchronousLoaderMap().end()) { - protocolMapItem->value(context, request, storedCredentials, error, response, data); + if (auto constructor = builtinResourceHandleSynchronousLoaderMap().get(request.url().protocol().toStringWithoutCopying())) { + constructor(context, request, storedCredentials, error, response, data); return; } @@ -147,24 +145,39 @@ ResourceHandleClient* ResourceHandle::client() const return d->m_client; } -void ResourceHandle::setClient(ResourceHandleClient* client) +void ResourceHandle::clearClient() { - d->m_client = client; + d->m_client = nullptr; } -#if !PLATFORM(MAC) && !USE(CFNETWORK) && !USE(SOUP) -// ResourceHandle never uses async client calls on these platforms yet. -void ResourceHandle::continueWillSendRequest(const ResourceRequest&) +void ResourceHandle::didReceiveResponse(ResourceResponse&& response) { - notImplemented(); + if (response.isHTTP09()) { + auto url = response.url(); + std::optional<uint16_t> port = url.port(); + if (port && !isDefaultPortForProtocol(port.value(), url.protocol())) { + cancel(); + String message = "Cancelled load from '" + url.stringCenterEllipsizedToLength() + "' because it is using HTTP/0.9."; + d->m_client->didFail(this, { String(), 0, url, message }); + return; + } + } + if (d->m_usesAsyncCallbacks) + d->m_client->didReceiveResponseAsync(this, WTFMove(response)); + else { + d->m_client->didReceiveResponse(this, WTFMove(response)); + platformContinueSynchronousDidReceiveResponse(); + } } -void ResourceHandle::continueDidReceiveResponse() +#if !PLATFORM(COCOA) && !USE(CFURLCONNECTION) && !USE(SOUP) +// ResourceHandle never uses async client calls on these platforms yet. +void ResourceHandle::continueWillSendRequest(ResourceRequest&&) { notImplemented(); } -void ResourceHandle::continueShouldUseCredentialStorage(bool) +void ResourceHandle::continueDidReceiveResponse() { notImplemented(); } @@ -177,6 +190,13 @@ void ResourceHandle::continueCanAuthenticateAgainstProtectionSpace(bool) #endif #endif +#if !USE(SOUP) +void ResourceHandle::platformContinueSynchronousDidReceiveResponse() +{ + // Do nothing. +} +#endif + ResourceRequest& ResourceHandle::firstRequest() { return d->m_firstRequest; @@ -199,7 +219,7 @@ bool ResourceHandle::hasAuthenticationChallenge() const void ResourceHandle::clearAuthentication() { -#if PLATFORM(MAC) +#if PLATFORM(COCOA) d->m_currentMacChallenge = nil; #endif d->m_currentWebChallenge.nullify(); @@ -212,7 +232,7 @@ bool ResourceHandle::shouldContentSniff() const bool ResourceHandle::shouldContentSniffURL(const URL& url) { -#if PLATFORM(MAC) +#if PLATFORM(COCOA) if (shouldForceContentSniffing) return true; #endif @@ -244,9 +264,9 @@ void ResourceHandle::setDefersLoading(bool defers) platformSetDefersLoading(defers); } -void ResourceHandle::didChangePriority(ResourceLoadPriority) +bool ResourceHandle::usesAsyncCallbacks() const { - // Optionally implemented by platform. + return d->m_usesAsyncCallbacks; } } // namespace WebCore diff --git a/Source/WebCore/platform/network/ResourceHandle.h b/Source/WebCore/platform/network/ResourceHandle.h index c1da03494..e1c429ad0 100644 --- a/Source/WebCore/platform/network/ResourceHandle.h +++ b/Source/WebCore/platform/network/ResourceHandle.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2011, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2004-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -23,19 +23,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceHandle_h -#define ResourceHandle_h +#pragma once #include "AuthenticationClient.h" #include "HTTPHeaderMap.h" #include "ResourceHandleTypes.h" #include "ResourceLoadPriority.h" -#include <wtf/OwnPtr.h> #include <wtf/RefCounted.h> -#if USE(QUICK_LOOK) -#include "QuickLook.h" -#endif // USE(QUICK_LOOK) +#if PLATFORM(COCOA) || USE(CFURLCONNECTION) +#include <wtf/RetainPtr.h> +#endif #if USE(SOUP) typedef struct _GTlsCertificate GTlsCertificate; @@ -47,20 +45,10 @@ typedef struct _SoupRequest SoupRequest; typedef const struct __CFData * CFDataRef; #endif -#if USE(WININET) -typedef unsigned long DWORD; -typedef unsigned long DWORD_PTR; -typedef void* LPVOID; -typedef LPVOID HINTERNET; -#endif - -#if PLATFORM(MAC) || USE(CFNETWORK) -#include <wtf/RetainPtr.h> -#endif - -#if PLATFORM(MAC) +#if PLATFORM(COCOA) OBJC_CLASS NSCachedURLResponse; OBJC_CLASS NSData; +OBJC_CLASS NSDictionary; OBJC_CLASS NSError; OBJC_CLASS NSURLConnection; #ifndef __OBJC__ @@ -68,14 +56,14 @@ typedef struct objc_object *id; #endif #endif -#if USE(CFNETWORK) +#if USE(CFURLCONNECTION) typedef const struct _CFCachedURLResponse* CFCachedURLResponseRef; typedef struct _CFURLConnection* CFURLConnectionRef; typedef int CFHTTPCookieStorageAcceptPolicy; typedef struct OpaqueCFHTTPCookieStorage* CFHTTPCookieStorageRef; #endif -#if PLATFORM(MAC) || USE(CFNETWORK) +#if PLATFORM(COCOA) || USE(CFURLCONNECTION) typedef const struct __CFURLStorageSession* CFURLStorageSessionRef; #endif @@ -84,76 +72,91 @@ class SchedulePair; } namespace WebCore { + class AuthenticationChallenge; class Credential; class Frame; class URL; class NetworkingContext; class ProtectionSpace; +class QuickLookHandle; class ResourceError; class ResourceHandleClient; class ResourceHandleInternal; +class NetworkLoadTiming; class ResourceRequest; class ResourceResponse; +class SoupNetworkSession; class SharedBuffer; +class Timer; -template <typename T> class Timer; +class ResourceHandle : public RefCounted<ResourceHandle>, public AuthenticationClient { +public: + WEBCORE_EXPORT static RefPtr<ResourceHandle> create(NetworkingContext*, const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff); + WEBCORE_EXPORT static void loadResourceSynchronously(NetworkingContext*, const ResourceRequest&, StoredCredentials, ResourceError&, ResourceResponse&, Vector<char>& data); -class ResourceHandle : public RefCounted<ResourceHandle> -#if PLATFORM(MAC) || USE(CFNETWORK) || USE(CURL) || USE(SOUP) - , public AuthenticationClient +#if USE(SOUP) + static RefPtr<ResourceHandle> create(SoupNetworkSession&, const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff); #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); - virtual ~ResourceHandle(); + WEBCORE_EXPORT virtual ~ResourceHandle(); -#if PLATFORM(MAC) || USE(CFNETWORK) - void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse); +#if PLATFORM(COCOA) || USE(CFURLCONNECTION) + ResourceRequest willSendRequest(ResourceRequest&&, ResourceResponse&&); #endif -#if PLATFORM(MAC) || USE(CFNETWORK) || USE(CURL) || USE(SOUP) + + void didReceiveResponse(ResourceResponse&&); + bool shouldUseCredentialStorage(); void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); - virtual void receivedCredential(const AuthenticationChallenge&, const Credential&) override; - virtual void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) override; - virtual void receivedCancellation(const AuthenticationChallenge&) override; + void receivedCredential(const AuthenticationChallenge&, const Credential&) override; + void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) override; + void receivedCancellation(const AuthenticationChallenge&) override; + void receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&) override; + void receivedChallengeRejection(const AuthenticationChallenge&) override; + +#if PLATFORM(COCOA) || USE(CFURLCONNECTION) + bool tryHandlePasswordBasedAuthentication(const AuthenticationChallenge&); #endif -#if PLATFORM(MAC) -#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +#if PLATFORM(COCOA) && USE(PROTECTION_SPACE_AUTH_CALLBACK) bool canAuthenticateAgainstProtectionSpace(const ProtectionSpace&); #endif -#if !USE(CFNETWORK) - void didCancelAuthenticationChallenge(const AuthenticationChallenge&); - NSURLConnection *connection() const; + +#if PLATFORM(COCOA) && !USE(CFURLCONNECTION) + WEBCORE_EXPORT NSURLConnection *connection() const; + id makeDelegate(bool); id delegate(); void releaseDelegate(); #endif - - void schedule(WTF::SchedulePair*); - void unschedule(WTF::SchedulePair*); + +#if PLATFORM(COCOA) && ENABLE(WEB_TIMING) +#if USE(CFURLCONNECTION) + static void getConnectionTimingData(CFURLConnectionRef, NetworkLoadTiming&); +#else + static void getConnectionTimingData(NSURLConnection *, NetworkLoadTiming&); +#endif +#endif + +#if PLATFORM(COCOA) + void schedule(WTF::SchedulePair&); + void unschedule(WTF::SchedulePair&); #endif -#if USE(CFNETWORK) + +#if USE(CFURLCONNECTION) CFURLStorageSessionRef storageSession() const; CFURLConnectionRef connection() const; - CFURLConnectionRef releaseConnectionForDownload(); + WEBCORE_EXPORT RetainPtr<CFURLConnectionRef> releaseConnectionForDownload(); const ResourceRequest& currentRequest() const; static void setHostAllowsAnyHTTPSCertificate(const String&); static void setClientCertificate(const String& host, CFDataRef); - -#if USE(QUICK_LOOK) - QuickLookHandle* quickLookHandle() { return m_quickLook.get(); } - void setQuickLookHandle(PassOwnPtr<QuickLookHandle> handle) { m_quickLook = handle; } -#endif // USE(QUICK_LOOK) - -#endif // USE(CFNETWORK) +#endif #if PLATFORM(WIN) && USE(CURL) static void setHostAllowsAnyHTTPSCertificate(const String&); static void setClientCertificateInfo(const String&, const String&, const String&); #endif + #if PLATFORM(WIN) && USE(CURL) && USE(CF) static void setClientCertificate(const String& host, CFDataRef); #endif @@ -161,90 +164,71 @@ public: bool shouldContentSniff() const; static bool shouldContentSniffURL(const URL&); - 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 + WEBCORE_EXPORT static void forceContentSniffing(); #if USE(CURL) || USE(SOUP) ResourceHandleInternal* getInternal() { return d.get(); } #endif #if USE(SOUP) + RefPtr<ResourceHandle> releaseForDownload(ResourceHandleClient*); void continueDidReceiveAuthenticationChallenge(const Credential& credentialFromPersistentStorage); void sendPendingRequest(); bool cancelledOrClientless(); void ensureReadBuffer(); size_t currentStreamPosition() const; void didStartRequest(); - static void setHostAllowsAnyHTTPSCertificate(const String&); - static void setClientCertificate(const String& host, GTlsCertificate*); - static void setIgnoreSSLErrors(bool); + double m_requestTime; #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(); - virtual void cancel(); + WEBCORE_EXPORT virtual void cancel(); // The client may be 0, in which case no callbacks will be made. - ResourceHandleClient* client() const; - void setClient(ResourceHandleClient*); + WEBCORE_EXPORT ResourceHandleClient* client() const; + WEBCORE_EXPORT void clearClient(); // Called in response to ResourceHandleClient::willSendRequestAsync(). - void continueWillSendRequest(const ResourceRequest&); + WEBCORE_EXPORT void continueWillSendRequest(ResourceRequest&&); // Called in response to ResourceHandleClient::didReceiveResponseAsync(). - void continueDidReceiveResponse(); - - // Called in response to ResourceHandleClient::shouldUseCredentialStorageAsync(). - void continueShouldUseCredentialStorage(bool); + WEBCORE_EXPORT virtual void continueDidReceiveResponse(); #if USE(PROTECTION_SPACE_AUTH_CALLBACK) // Called in response to ResourceHandleClient::canAuthenticateAgainstProtectionSpaceAsync(). - void continueCanAuthenticateAgainstProtectionSpace(bool); + WEBCORE_EXPORT void continueCanAuthenticateAgainstProtectionSpace(bool); #endif // Called in response to ResourceHandleClient::willCacheResponseAsync(). -#if USE(CFNETWORK) - void continueWillCacheResponse(CFCachedURLResponseRef); -#elif PLATFORM(MAC) - void continueWillCacheResponse(NSCachedURLResponse *); +#if USE(CFURLCONNECTION) + WEBCORE_EXPORT void continueWillCacheResponse(CFCachedURLResponseRef); +#endif +#if PLATFORM(COCOA) && !USE(CFURLCONNECTION) + WEBCORE_EXPORT void continueWillCacheResponse(NSCachedURLResponse *); #endif - void setDefersLoading(bool); - - void didChangePriority(ResourceLoadPriority); + WEBCORE_EXPORT void setDefersLoading(bool); - ResourceRequest& firstRequest(); + WEBCORE_EXPORT ResourceRequest& firstRequest(); const String& lastHTTPMethod() const; - void failureTimerFired(Timer<ResourceHandle>&); + void failureTimerFired(); NetworkingContext* context() const; using RefCounted<ResourceHandle>::ref; using RefCounted<ResourceHandle>::deref; -#if PLATFORM(MAC) || USE(CFNETWORK) - static CFStringRef synchronousLoadRunLoopMode(); -#endif -#if PLATFORM(IOS) && USE(CFNETWORK) - static CFMutableDictionaryRef createSSLPropertiesFromNSURLRequest(const ResourceRequest&); +#if PLATFORM(COCOA) || USE(CFURLCONNECTION) + WEBCORE_EXPORT static CFStringRef synchronousLoadRunLoopMode(); #endif -#if USE(NETWORK_CFDATA_ARRAY_CALLBACK) - void handleDataArray(CFArrayRef dataArray); +#if PLATFORM(IOS) && USE(CFURLCONNECTION) + static CFMutableDictionaryRef createSSLPropertiesFromNSURLRequest(const ResourceRequest&); #endif - typedef PassRefPtr<ResourceHandle> (*BuiltinConstructor)(const ResourceRequest& request, ResourceHandleClient* client); + typedef Ref<ResourceHandle> (*BuiltinConstructor)(const ResourceRequest& request, ResourceHandleClient* client); static void registerBuiltinConstructor(const AtomicString& protocol, BuiltinConstructor); typedef void (*BuiltinSynchronousLoader)(NetworkingContext*, const ResourceRequest&, StoredCredentials, ResourceError&, ResourceResponse&, Vector<char>& data); @@ -253,6 +237,8 @@ public: protected: ResourceHandle(NetworkingContext*, const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff); + bool usesAsyncCallbacks() const; + private: enum FailureType { NoFailure, @@ -260,37 +246,44 @@ private: InvalidURLFailure }; +#if USE(SOUP) + ResourceHandle(SoupNetworkSession&, const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff); +#endif + void platformSetDefersLoading(bool); + void platformContinueSynchronousDidReceiveResponse(); + void scheduleFailure(FailureType); bool start(); static void platformLoadResourceSynchronously(NetworkingContext*, const ResourceRequest&, StoredCredentials, ResourceError&, ResourceResponse&, Vector<char>& data); - virtual void refAuthenticationClient() override { ref(); } - virtual void derefAuthenticationClient() override { deref(); } + void refAuthenticationClient() override { ref(); } + void derefAuthenticationClient() override { deref(); } -#if PLATFORM(MAC) || USE(CFNETWORK) - enum class SchedulingBehavior { - Asynchronous, - Synchronous - }; +#if PLATFORM(COCOA) || USE(CFURLCONNECTION) + enum class SchedulingBehavior { Asynchronous, Synchronous }; +#endif -#if USE(CFNETWORK) +#if USE(CFURLCONNECTION) void createCFURLConnection(bool shouldUseCredentialStorage, bool shouldContentSniff, SchedulingBehavior, CFDictionaryRef clientProperties); -#else +#endif + +#if PLATFORM(MAC) && !USE(CFURLCONNECTION) void createNSURLConnection(id delegate, bool shouldUseCredentialStorage, bool shouldContentSniff, SchedulingBehavior); #endif + +#if PLATFORM(IOS) && !USE(CFURLCONNECTION) + void createNSURLConnection(id delegate, bool shouldUseCredentialStorage, bool shouldContentSniff, SchedulingBehavior, NSDictionary *connectionProperties); #endif - friend class ResourceHandleInternal; - OwnPtr<ResourceHandleInternal> d; +#if USE(SOUP) + void timeoutFired(); +#endif -#if USE(QUICK_LOOK) - OwnPtr<QuickLookHandle> m_quickLook; -#endif // USE(QUICK_LOOK) + friend class ResourceHandleInternal; + std::unique_ptr<ResourceHandleInternal> d; }; } - -#endif // ResourceHandle_h diff --git a/Source/WebCore/platform/network/ResourceHandleClient.cpp b/Source/WebCore/platform/network/ResourceHandleClient.cpp index 8eb4eeb6a..9487d967e 100644 --- a/Source/WebCore/platform/network/ResourceHandleClient.cpp +++ b/Source/WebCore/platform/network/ResourceHandleClient.cpp @@ -27,6 +27,7 @@ #include "ResourceHandleClient.h" #include "ResourceHandle.h" +#include "ResourceRequest.h" #include "SharedBuffer.h" namespace WebCore { @@ -38,20 +39,20 @@ ResourceHandleClient::ResourceHandleClient() ResourceHandleClient::~ResourceHandleClient() { } - -void ResourceHandleClient::willSendRequestAsync(ResourceHandle* handle, const ResourceRequest& request, const ResourceResponse& /*redirectResponse*/) + +ResourceRequest ResourceHandleClient::willSendRequest(ResourceHandle*, ResourceRequest&& request, ResourceResponse&&) { - handle->continueWillSendRequest(request); + return WTFMove(request); } -void ResourceHandleClient::didReceiveResponseAsync(ResourceHandle* handle, const ResourceResponse&) +void ResourceHandleClient::willSendRequestAsync(ResourceHandle* handle, ResourceRequest&& request, ResourceResponse&& /*redirectResponse*/) { - handle->continueDidReceiveResponse(); + handle->continueWillSendRequest(WTFMove(request)); } -void ResourceHandleClient::shouldUseCredentialStorageAsync(ResourceHandle* handle) +void ResourceHandleClient::didReceiveResponseAsync(ResourceHandle* handle, ResourceResponse&&) { - handle->continueShouldUseCredentialStorage(false); + handle->continueDidReceiveResponse(); } #if USE(PROTECTION_SPACE_AUTH_CALLBACK) @@ -61,19 +62,19 @@ void ResourceHandleClient::canAuthenticateAgainstProtectionSpaceAsync(ResourceHa } #endif -#if USE(CFNETWORK) +#if USE(CFURLCONNECTION) void ResourceHandleClient::willCacheResponseAsync(ResourceHandle* handle, CFCachedURLResponseRef response) { handle->continueWillCacheResponse(response); } -#elif PLATFORM(MAC) +#elif PLATFORM(COCOA) void ResourceHandleClient::willCacheResponseAsync(ResourceHandle* handle, NSCachedURLResponse *response) { handle->continueWillCacheResponse(response); } #endif -void ResourceHandleClient::didReceiveBuffer(ResourceHandle* handle, PassRefPtr<SharedBuffer> buffer, int encodedDataLength) +void ResourceHandleClient::didReceiveBuffer(ResourceHandle* handle, Ref<SharedBuffer>&& buffer, int encodedDataLength) { didReceiveData(handle, buffer->data(), buffer->size(), encodedDataLength); } diff --git a/Source/WebCore/platform/network/ResourceHandleClient.h b/Source/WebCore/platform/network/ResourceHandleClient.h index aa4b79a87..5c371ed99 100644 --- a/Source/WebCore/platform/network/ResourceHandleClient.h +++ b/Source/WebCore/platform/network/ResourceHandleClient.h @@ -1,6 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2013 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -24,18 +23,21 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceHandleClient_h -#define ResourceHandleClient_h +#pragma once -#include <wtf/PassRefPtr.h> +#include "PlatformExportMacros.h" +#include <wtf/Ref.h> -#if USE(CFNETWORK) +#if USE(CFURLCONNECTION) #include <CFNetwork/CFURLCachePriv.h> #include <CFNetwork/CFURLResponsePriv.h> +#endif + +#if PLATFORM(IOS) || USE(CFURLCONNECTION) #include <wtf/RetainPtr.h> #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) OBJC_CLASS NSCachedURLResponse; #endif @@ -58,17 +60,16 @@ namespace WebCore { class ResourceHandleClient { public: - ResourceHandleClient(); - virtual ~ResourceHandleClient(); + WEBCORE_EXPORT ResourceHandleClient(); + WEBCORE_EXPORT virtual ~ResourceHandleClient(); - // Request may be modified. - virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/) { } + WEBCORE_EXPORT virtual ResourceRequest willSendRequest(ResourceHandle*, ResourceRequest&&, ResourceResponse&&); virtual void didSendData(ResourceHandle*, unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/) { } - virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&) { } + virtual void didReceiveResponse(ResourceHandle*, ResourceResponse&&) { } virtual void didReceiveData(ResourceHandle*, const char*, unsigned, int /*encodedDataLength*/) { } - virtual void didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer>, int encodedDataLength); + WEBCORE_EXPORT virtual void didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&&, int encodedDataLength); virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/) { } virtual void didFail(ResourceHandle*, const ResourceError&) { } @@ -77,23 +78,23 @@ namespace WebCore { virtual bool usesAsyncCallbacks() { return false; } + virtual bool loadingSynchronousXHR() { return false; } + // Client will pass an updated request using ResourceHandle::continueWillSendRequest() when ready. - virtual void willSendRequestAsync(ResourceHandle*, const ResourceRequest&, const ResourceResponse& redirectResponse); + WEBCORE_EXPORT virtual void willSendRequestAsync(ResourceHandle*, ResourceRequest&&, ResourceResponse&&); // Client will call ResourceHandle::continueDidReceiveResponse() when ready. - virtual void didReceiveResponseAsync(ResourceHandle*, const ResourceResponse&); + WEBCORE_EXPORT virtual void didReceiveResponseAsync(ResourceHandle*, ResourceResponse&&); - // Client will pass an updated request using ResourceHandle::continueShouldUseCredentialStorage() when ready. - virtual void shouldUseCredentialStorageAsync(ResourceHandle*); #if USE(PROTECTION_SPACE_AUTH_CALLBACK) // Client will pass an updated request using ResourceHandle::continueCanAuthenticateAgainstProtectionSpace() when ready. - virtual void canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle*, const ProtectionSpace&); + WEBCORE_EXPORT virtual void canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle*, const ProtectionSpace&); #endif // Client will pass an updated request using ResourceHandle::continueWillCacheResponse() when ready. -#if USE(CFNETWORK) - virtual void willCacheResponseAsync(ResourceHandle*, CFCachedURLResponseRef); -#elif PLATFORM(MAC) - virtual void willCacheResponseAsync(ResourceHandle*, NSCachedURLResponse *); +#if USE(CFURLCONNECTION) + WEBCORE_EXPORT virtual void willCacheResponseAsync(ResourceHandle*, CFCachedURLResponseRef); +#elif PLATFORM(COCOA) + WEBCORE_EXPORT virtual void willCacheResponseAsync(ResourceHandle*, NSCachedURLResponse *); #endif #if USE(NETWORK_CFDATA_ARRAY_CALLBACK) @@ -107,26 +108,24 @@ namespace WebCore { 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 USE(CFNETWORK) - virtual CFCachedURLResponseRef willCacheResponse(ResourceHandle*, CFCachedURLResponseRef response) { return response; } +#if PLATFORM(IOS) || USE(CFURLCONNECTION) virtual RetainPtr<CFDictionaryRef> connectionProperties(ResourceHandle*) { return nullptr; } +#endif + +#if USE(CFURLCONNECTION) + virtual CFCachedURLResponseRef willCacheResponse(ResourceHandle*, CFCachedURLResponseRef response) { return response; } #if PLATFORM(WIN) virtual bool shouldCacheResponse(ResourceHandle*, CFCachedURLResponseRef) { return true; } #endif // PLATFORM(WIN) -#elif PLATFORM(MAC) +#elif PLATFORM(COCOA) virtual NSCachedURLResponse *willCacheResponse(ResourceHandle*, NSCachedURLResponse *response) { return response; } - virtual void willStopBufferingData(ResourceHandle*, const char*, unsigned) { } #endif - }; } - -#endif diff --git a/Source/WebCore/platform/network/ResourceHandleInternal.h b/Source/WebCore/platform/network/ResourceHandleInternal.h index 6c1ba947f..d84de90cd 100644 --- a/Source/WebCore/platform/network/ResourceHandleInternal.h +++ b/Source/WebCore/platform/network/ResourceHandleInternal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -23,21 +23,21 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceHandleInternal_h -#define ResourceHandleInternal_h +#pragma once #include "NetworkingContext.h" #include "ResourceHandle.h" +#include "ResourceHandleClient.h" #include "ResourceRequest.h" #include "AuthenticationChallenge.h" #include "Timer.h" -#if USE(CFNETWORK) +#if USE(CFURLCONNECTION) #include "ResourceHandleCFURLConnectionDelegate.h" #include <CFNetwork/CFURLConnectionPriv.h> #endif -#if USE(WININET) || (USE(CURL) && PLATFORM(WIN)) +#if USE(CURL) && PLATFORM(WIN) #include <winsock2.h> #include <windows.h> #endif @@ -50,17 +50,18 @@ #if USE(SOUP) #include "GUniquePtrSoup.h" +#include "SoupNetworkSession.h" #include <libsoup/soup.h> -#include <wtf/gobject/GRefPtr.h> -class Frame; +#include <wtf/RunLoop.h> +#include <wtf/glib/GRefPtr.h> #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) OBJC_CLASS NSURLAuthenticationChallenge; OBJC_CLASS NSURLConnection; #endif -#if PLATFORM(MAC) || USE(CFNETWORK) +#if PLATFORM(COCOA) || USE(CFURLCONNECTION) typedef const struct __CFURLStorageSession* CFURLStorageSessionRef; #endif @@ -69,160 +70,120 @@ typedef const struct __CFURLStorageSession* CFURLStorageSessionRef; // WebCoreResourceLoaderImp which avoids doing work in dealloc). namespace WebCore { - class ResourceHandleClient; - - class ResourceHandleInternal { - WTF_MAKE_NONCOPYABLE(ResourceHandleInternal); WTF_MAKE_FAST_ALLOCATED; - public: - ResourceHandleInternal(ResourceHandle* loader, NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) - : m_context(context) - , m_client(client) - , m_firstRequest(request) - , m_lastHTTPMethod(request.httpMethod()) - , status(0) - , m_defersLoading(defersLoading) - , m_shouldContentSniff(shouldContentSniff) -#if USE(CFNETWORK) - , m_connection(0) - , m_currentRequest(request) -#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) + +class ResourceHandleInternal { + WTF_MAKE_NONCOPYABLE(ResourceHandleInternal); WTF_MAKE_FAST_ALLOCATED; +public: + ResourceHandleInternal(ResourceHandle* loader, NetworkingContext* context, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) + : m_context(context) + , m_client(client) + , m_firstRequest(request) + , m_lastHTTPMethod(request.httpMethod()) + , m_partition(request.cachePartition()) + , m_defersLoading(defersLoading) + , m_shouldContentSniff(shouldContentSniff) + , m_usesAsyncCallbacks(client && client->usesAsyncCallbacks()) +#if USE(CFURLCONNECTION) + , m_currentRequest(request) #endif #if USE(CURL) - , m_handle(0) - , m_url(0) - , m_customHeaders(0) - , m_cancelled(false) - , m_authFailureCount(0) - , m_formDataStream(loader) - , m_sslErrors(0) + , m_formDataStream(loader) #endif #if USE(SOUP) - , m_cancelled(false) - , m_bodySize(0) - , m_bodyDataSent(0) - , m_redirectCount(0) - , m_previousPosition(0) - , m_useAuthenticationManager(true) -#endif -#if PLATFORM(MAC) - , m_startWhenScheduled(false) - , m_needsSiteSpecificQuirks(false) - , m_currentMacChallenge(nil) -#endif - , m_scheduledFailureType(ResourceHandle::NoFailure) - , m_failureTimer(loader, &ResourceHandle::failureTimerFired) - { - const URL& url = m_firstRequest.url(); - m_user = url.user(); - m_pass = url.pass(); - m_firstRequest.removeCredentials(); - } - - ~ResourceHandleInternal(); - - ResourceHandleClient* client() { return m_client; } - - RefPtr<NetworkingContext> m_context; - 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; - ResourceRequest m_currentRequest; - RefPtr<ResourceHandleCFURLConnectionDelegate> m_connectionDelegate; -#endif -#if PLATFORM(MAC) && !USE(CFNETWORK) - RetainPtr<NSURLConnection> m_connection; - RetainPtr<id> m_delegate; -#endif -#if PLATFORM(MAC) - bool m_startWhenScheduled; - bool m_needsSiteSpecificQuirks; -#endif -#if PLATFORM(MAC) || USE(CFNETWORK) - RetainPtr<CFURLStorageSessionRef> m_storageSession; -#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; + , m_timeoutSource(RunLoop::main(), loader, &ResourceHandle::timeoutFired) +#endif + , m_failureTimer(*loader, &ResourceHandle::failureTimerFired) + { + const URL& url = m_firstRequest.url(); + m_user = url.user(); + m_pass = url.pass(); + m_firstRequest.removeCredentials(); + } + + ~ResourceHandleInternal(); + + ResourceHandleClient* client() { return m_client; } + + RefPtr<NetworkingContext> m_context; + ResourceHandleClient* m_client; + ResourceRequest m_firstRequest; + String m_lastHTTPMethod; + String m_partition; + + // Suggested credentials for the current redirection step. + String m_user; + String m_pass; + + Credential m_initialCredential; + + int status { 0 }; + + bool m_defersLoading; + bool m_shouldContentSniff; + bool m_usesAsyncCallbacks; +#if USE(CFURLCONNECTION) + RetainPtr<CFURLConnectionRef> m_connection; + ResourceRequest m_currentRequest; + RefPtr<ResourceHandleCFURLConnectionDelegate> m_connectionDelegate; +#endif +#if PLATFORM(COCOA) && !USE(CFURLCONNECTION) + RetainPtr<NSURLConnection> m_connection; + RetainPtr<id> m_delegate; +#endif +#if PLATFORM(COCOA) + bool m_startWhenScheduled { false }; +#endif +#if PLATFORM(COCOA) || USE(CFURLCONNECTION) + RetainPtr<CFURLStorageSessionRef> m_storageSession; #endif #if USE(CURL) - CURL* m_handle; - char* m_url; - struct curl_slist* m_customHeaders; - ResourceResponse m_response; - bool m_cancelled; - unsigned short m_authFailureCount; + CURL* m_handle { nullptr }; + char* m_url { nullptr }; + struct curl_slist* m_customHeaders { nullptr }; + ResourceResponse m_response; + bool m_cancelled { false }; + unsigned short m_authFailureCount { 0 }; - FormDataStream m_formDataStream; - unsigned m_sslErrors; - Vector<char> m_postBytes; + FormDataStream m_formDataStream; + unsigned m_sslErrors { 0 }; + Vector<char> m_postBytes; - OwnPtr<MultipartHandle> m_multipartHandle; + std::unique_ptr<MultipartHandle> m_multipartHandle; + bool m_addedCacheValidationHeaders { false }; #endif #if USE(SOUP) - GRefPtr<SoupMessage> m_soupMessage; - ResourceResponse m_response; - bool m_cancelled; - GRefPtr<SoupRequest> m_soupRequest; - GRefPtr<GInputStream> m_inputStream; - GRefPtr<SoupMultipartInputStream> m_multipartInputStream; - GRefPtr<GCancellable> m_cancellable; - GRefPtr<GAsyncResult> m_deferredResult; - GRefPtr<GSource> m_timeoutSource; - GUniquePtr<SoupBuffer> m_soupBuffer; - unsigned long m_bodySize; - unsigned long m_bodyDataSent; - SoupSession* soupSession(); - int m_redirectCount; - size_t m_previousPosition; - bool m_useAuthenticationManager; -#endif -#if PLATFORM(GTK) - struct { - Credential credential; - AuthenticationChallenge challenge; - } m_credentialDataToSaveInPersistentStore; -#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 - AuthenticationChallenge m_currentWebChallenge; - ResourceHandle::FailureType m_scheduledFailureType; - Timer<ResourceHandle> m_failureTimer; - }; + SoupNetworkSession* m_session { nullptr }; + GRefPtr<SoupMessage> m_soupMessage; + ResourceResponse m_response; + bool m_cancelled { false }; + GRefPtr<SoupRequest> m_soupRequest; + GRefPtr<GInputStream> m_inputStream; + GRefPtr<SoupMultipartInputStream> m_multipartInputStream; + GRefPtr<GCancellable> m_cancellable; + GRefPtr<GAsyncResult> m_deferredResult; + RunLoop::Timer<ResourceHandle> m_timeoutSource; + GUniquePtr<SoupBuffer> m_soupBuffer; + unsigned long m_bodySize { 0 }; + unsigned long m_bodyDataSent { 0 }; + SoupSession* soupSession(); + int m_redirectCount { 0 }; + size_t m_previousPosition { 0 }; + bool m_useAuthenticationManager { true }; + struct { + Credential credential; + ProtectionSpace protectionSpace; + } m_credentialDataToSaveInPersistentStore; +#endif + +#if PLATFORM(COCOA) + // 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 { nil }; +#endif + + AuthenticationChallenge m_currentWebChallenge; + ResourceHandle::FailureType m_scheduledFailureType { ResourceHandle::NoFailure }; + Timer m_failureTimer; +}; } // namespace WebCore - -#endif // ResourceHandleInternal_h diff --git a/Source/WebCore/platform/network/ResourceHandleTypes.h b/Source/WebCore/platform/network/ResourceHandleTypes.h index 0c31f92e6..870e7957d 100644 --- a/Source/WebCore/platform/network/ResourceHandleTypes.h +++ b/Source/WebCore/platform/network/ResourceHandleTypes.h @@ -33,12 +33,6 @@ enum StoredCredentials { DoNotAllowStoredCredentials }; -enum ClientCredentialPolicy { - AskClientForAllCredentials, - DoNotAskClientForCrossOriginCredentials, - DoNotAskClientForAnyCredentials -}; - } // namespace WebCore #endif // ResourceHandleTypes_h diff --git a/Source/WebCore/platform/network/ResourceLoadPriority.h b/Source/WebCore/platform/network/ResourceLoadPriority.h index 5a7104f97..86d74a0c8 100644 --- a/Source/WebCore/platform/network/ResourceLoadPriority.h +++ b/Source/WebCore/platform/network/ResourceLoadPriority.h @@ -28,18 +28,30 @@ namespace WebCore { -enum ResourceLoadPriority { - // The unresolved priority is here for the convenience of the clients. It should not be passed to the ResourceLoadScheduler. - ResourceLoadPriorityUnresolved = -1, - ResourceLoadPriorityVeryLow = 0, - ResourceLoadPriorityLow, - ResourceLoadPriorityMedium, - ResourceLoadPriorityHigh, - ResourceLoadPriorityVeryHigh, - ResourceLoadPriorityLowest = ResourceLoadPriorityVeryLow, - ResourceLoadPriorityHighest = ResourceLoadPriorityVeryHigh, +enum class ResourceLoadPriority { + VeryLow, + Low, + Medium, + High, + VeryHigh, + Lowest = VeryLow, + Highest = VeryHigh, }; +static const unsigned resourceLoadPriorityCount { static_cast<unsigned>(ResourceLoadPriority::Highest) + 1 }; + +inline ResourceLoadPriority& operator++(ResourceLoadPriority& priority) +{ + ASSERT(priority != ResourceLoadPriority::Highest); + return priority = static_cast<ResourceLoadPriority>(static_cast<int>(priority) + 1); +} + +inline ResourceLoadPriority& operator--(ResourceLoadPriority& priority) +{ + ASSERT(priority != ResourceLoadPriority::Lowest); + return priority = static_cast<ResourceLoadPriority>(static_cast<int>(priority) - 1); +} + } #endif diff --git a/Source/WebCore/platform/network/ResourceLoadTiming.h b/Source/WebCore/platform/network/ResourceLoadTiming.h deleted file mode 100644 index 2a395ef7c..000000000 --- a/Source/WebCore/platform/network/ResourceLoadTiming.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 DocumentLoadTiming; - -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); - } - - // We want to present a unified timeline to Javascript. Using walltime is problematic, because the clock may skew while resources - // load. To prevent that skew, we record a single reference walltime when root document navigation begins. All other times are - // recorded using monotonicallyIncreasingTime(). When a time needs to be presented to Javascript, we build a pseudo-walltime - // using the following equation: - // pseudo time = document wall reference + (resource request time - document monotonic reference) + deltaMilliseconds / 1000.0. - double convertResourceLoadTimeToMonotonicTime(int deltaMilliseconds) const; - - double requestTime; // monotonicallyIncreasingTime() when the port started handling this request. - int proxyStart; // The rest of these are millisecond deltas, using monotonicallyIncreasingTime(), from requestTime. - 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 index a956bb8c0..6914671c2 100644 --- a/Source/WebCore/platform/network/ResourceRequestBase.cpp +++ b/Source/WebCore/platform/network/ResourceRequestBase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006 Apple Inc. All rights reserved. * Copyright (C) 2009, 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -26,11 +26,14 @@ #include "config.h" #include "ResourceRequestBase.h" +#include "HTTPHeaderNames.h" +#include "PublicSuffix.h" #include "ResourceRequest.h" +#include <wtf/PointerComparison.h> namespace WebCore { -#if !USE(SOUP) && (!PLATFORM(MAC) || USE(CFNETWORK)) +#if !USE(SOUP) && (!PLATFORM(MAC) || USE(CFURLCONNECTION)) double ResourceRequestBase::s_defaultTimeoutInterval = INT_MAX; #else // Will use NSURLRequest default timeout unless set to a non-zero value with setDefaultTimeoutInterval(). @@ -47,58 +50,44 @@ inline const ResourceRequest& ResourceRequestBase::asResourceRequest() const return *static_cast<const ResourceRequest*>(this); } -PassOwnPtr<ResourceRequest> ResourceRequestBase::adopt(PassOwnPtr<CrossThreadResourceRequestData> data) +ResourceRequest ResourceRequestBase::isolatedCopy() const { - OwnPtr<ResourceRequest> request = adoptPtr(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->setPriority(data->m_priority); + ResourceRequest request; + request.setAsIsolatedCopy(asResourceRequest()); + return request; +} + +void ResourceRequestBase::setAsIsolatedCopy(const ResourceRequest& other) +{ + setURL(other.url().isolatedCopy()); + setCachePolicy(other.cachePolicy()); + setTimeoutInterval(other.timeoutInterval()); + setFirstPartyForCookies(other.firstPartyForCookies().isolatedCopy()); + setHTTPMethod(other.httpMethod().isolatedCopy()); + setPriority(other.priority()); + setRequester(other.requester()); + setInitiatorIdentifier(other.initiatorIdentifier().isolatedCopy()); + setCachePartition(other.cachePartition()); - request->updateResourceRequest(); - request->m_httpHeaderFields.adopt(data->m_httpHeaders.release()); + updateResourceRequest(); + m_httpHeaderFields = other.httpHeaderFields().isolatedCopy(); - size_t encodingCount = data->m_responseContentDispositionEncodingFallbackArray.size(); + size_t encodingCount = other.m_responseContentDispositionEncodingFallbackArray.size(); if (encodingCount > 0) { - String encoding1 = data->m_responseContentDispositionEncodingFallbackArray[0]; + String encoding1 = other.m_responseContentDispositionEncodingFallbackArray[0].isolatedCopy(); String encoding2; String encoding3; if (encodingCount > 1) { - encoding2 = data->m_responseContentDispositionEncodingFallbackArray[1]; + encoding2 = other.m_responseContentDispositionEncodingFallbackArray[1].isolatedCopy(); if (encodingCount > 2) - encoding3 = data->m_responseContentDispositionEncodingFallbackArray[2]; + encoding3 = other.m_responseContentDispositionEncodingFallbackArray[2].isolatedCopy(); } ASSERT(encodingCount <= 3); - request->setResponseContentDispositionEncodingFallbackArray(encoding1, encoding2, encoding3); + 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 = adoptPtr(new CrossThreadResourceRequestData()); - data->m_url = url().copy(); - data->m_cachePolicy = cachePolicy(); - data->m_timeoutInterval = timeoutInterval(); - data->m_firstPartyForCookies = firstPartyForCookies().copy(); - data->m_httpMethod = httpMethod().isolatedCopy(); - data->m_httpHeaders = httpHeaderFields().copyData(); - data->m_priority = priority(); - - 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].isolatedCopy()); - } - if (m_httpBody) - data->m_httpBody = m_httpBody->deepCopy(); - data->m_allowCookies = m_allowCookies; - return asResourceRequest().doPlatformCopyData(data.release()); + if (other.m_httpBody) + setHTTPBody(other.m_httpBody->isolatedCopy()); + setAllowCookies(other.m_allowCookies); } bool ResourceRequestBase::isEmpty() const @@ -148,7 +137,7 @@ ResourceRequestCachePolicy ResourceRequestBase::cachePolicy() const { updateResourceRequest(); - return m_cachePolicy; + return m_cachePolicy; } void ResourceRequestBase::setCachePolicy(ResourceRequestCachePolicy cachePolicy) @@ -230,94 +219,171 @@ const HTTPHeaderMap& ResourceRequestBase::httpHeaderFields() const return m_httpHeaderFields; } -String ResourceRequestBase::httpHeaderField(const AtomicString& name) const +String ResourceRequestBase::httpHeaderField(const String& name) const { updateResourceRequest(); return m_httpHeaderFields.get(name); } -String ResourceRequestBase::httpHeaderField(const char* name) const +String ResourceRequestBase::httpHeaderField(HTTPHeaderName name) const { updateResourceRequest(); return m_httpHeaderFields.get(name); } -void ResourceRequestBase::setHTTPHeaderField(const AtomicString& name, const String& value) +void ResourceRequestBase::setHTTPHeaderField(const String& name, const String& value) { - updateResourceRequest(); - - m_httpHeaderFields.set(name, value); + updateResourceRequest(); + + m_httpHeaderFields.set(name, value); if (url().protocolIsInHTTPFamily()) m_platformRequestUpdated = false; } -void ResourceRequestBase::setHTTPHeaderField(const char* name, const String& value) +void ResourceRequestBase::setHTTPHeaderField(HTTPHeaderName name, const String& value) { - setHTTPHeaderField(AtomicString(name), value); + updateResourceRequest(); + + m_httpHeaderFields.set(name, value); + + if (url().protocolIsInHTTPFamily()) + m_platformRequestUpdated = false; } void ResourceRequestBase::clearHTTPAuthorization() { updateResourceRequest(); - HTTPHeaderMap::iterator iter = m_httpHeaderFields.find("Authorization"); - if (iter == m_httpHeaderFields.end()) + if (!m_httpHeaderFields.remove(HTTPHeaderName::Authorization)) return; - m_httpHeaderFields.remove(iter); - if (url().protocolIsInHTTPFamily()) m_platformRequestUpdated = false; } +String ResourceRequestBase::httpContentType() const +{ + return httpHeaderField(HTTPHeaderName::ContentType); +} + +void ResourceRequestBase::setHTTPContentType(const String& httpContentType) +{ + setHTTPHeaderField(HTTPHeaderName::ContentType, httpContentType); +} + void ResourceRequestBase::clearHTTPContentType() { updateResourceRequest(); - m_httpHeaderFields.remove("Content-Type"); + m_httpHeaderFields.remove(HTTPHeaderName::ContentType); if (url().protocolIsInHTTPFamily()) m_platformRequestUpdated = false; } +String ResourceRequestBase::httpReferrer() const +{ + return httpHeaderField(HTTPHeaderName::Referer); +} + +bool ResourceRequestBase::hasHTTPReferrer() const +{ + return m_httpHeaderFields.contains(HTTPHeaderName::Referer); +} + +void ResourceRequestBase::setHTTPReferrer(const String& httpReferrer) +{ + setHTTPHeaderField(HTTPHeaderName::Referer, httpReferrer); +} + void ResourceRequestBase::clearHTTPReferrer() { updateResourceRequest(); - m_httpHeaderFields.remove("Referer"); + m_httpHeaderFields.remove(HTTPHeaderName::Referer); if (url().protocolIsInHTTPFamily()) m_platformRequestUpdated = false; } +String ResourceRequestBase::httpOrigin() const +{ + return httpHeaderField(HTTPHeaderName::Origin); +} + +void ResourceRequestBase::setHTTPOrigin(const String& httpOrigin) +{ + setHTTPHeaderField(HTTPHeaderName::Origin, httpOrigin); +} + +bool ResourceRequestBase::hasHTTPOrigin() const +{ + return m_httpHeaderFields.contains(HTTPHeaderName::Origin); +} + void ResourceRequestBase::clearHTTPOrigin() { - updateResourceRequest(); + updateResourceRequest(); - m_httpHeaderFields.remove("Origin"); + m_httpHeaderFields.remove(HTTPHeaderName::Origin); if (url().protocolIsInHTTPFamily()) m_platformRequestUpdated = false; } +bool ResourceRequestBase::hasHTTPHeader(HTTPHeaderName name) const +{ + return m_httpHeaderFields.contains(name); +} + +String ResourceRequestBase::httpUserAgent() const +{ + return httpHeaderField(HTTPHeaderName::UserAgent); +} + +void ResourceRequestBase::setHTTPUserAgent(const String& httpUserAgent) +{ + setHTTPHeaderField(HTTPHeaderName::UserAgent, httpUserAgent); +} + void ResourceRequestBase::clearHTTPUserAgent() { updateResourceRequest(); - m_httpHeaderFields.remove("User-Agent"); + m_httpHeaderFields.remove(HTTPHeaderName::UserAgent); if (url().protocolIsInHTTPFamily()) m_platformRequestUpdated = false; } +String ResourceRequestBase::httpAccept() const +{ + return httpHeaderField(HTTPHeaderName::Accept); +} + +void ResourceRequestBase::setHTTPAccept(const String& httpAccept) +{ + setHTTPHeaderField(HTTPHeaderName::Accept, httpAccept); +} + void ResourceRequestBase::clearHTTPAccept() { updateResourceRequest(); - m_httpHeaderFields.remove("Accept"); + m_httpHeaderFields.remove(HTTPHeaderName::Accept); + + if (url().protocolIsInHTTPFamily()) + m_platformRequestUpdated = false; +} + +void ResourceRequestBase::clearHTTPAcceptEncoding() +{ + updateResourceRequest(); + + m_httpHeaderFields.remove(HTTPHeaderName::AcceptEncoding); if (url().protocolIsInHTTPFamily()) m_platformRequestUpdated = false; @@ -347,11 +413,11 @@ FormData* ResourceRequestBase::httpBody() const return m_httpBody.get(); } -void ResourceRequestBase::setHTTPBody(PassRefPtr<FormData> httpBody) +void ResourceRequestBase::setHTTPBody(RefPtr<FormData>&& httpBody) { updateResourceRequest(); - m_httpBody = httpBody; + m_httpBody = WTFMove(httpBody); m_resourceRequestBodyUpdated = true; @@ -399,22 +465,50 @@ void ResourceRequestBase::setPriority(ResourceLoadPriority priority) m_platformRequestUpdated = false; } -void ResourceRequestBase::addHTTPHeaderField(const AtomicString& name, const String& value) +void ResourceRequestBase::addHTTPHeaderFieldIfNotPresent(HTTPHeaderName name, const String& value) +{ + updateResourceRequest(); + + if (!m_httpHeaderFields.addIfNotPresent(name, value)) + return; + + if (url().protocolIsInHTTPFamily()) + m_platformRequestUpdated = false; +} + +void ResourceRequestBase::addHTTPHeaderField(HTTPHeaderName name, const String& value) +{ + updateResourceRequest(); + + m_httpHeaderFields.add(name, value); + + if (url().protocolIsInHTTPFamily()) + m_platformRequestUpdated = false; +} + +void ResourceRequestBase::addHTTPHeaderField(const String& name, const String& value) { updateResourceRequest(); - HTTPHeaderMap::AddResult result = m_httpHeaderFields.add(name, value); - if (!result.isNewEntry) - result.iterator->value = result.iterator->value + ',' + value; + + m_httpHeaderFields.add(name, value); if (url().protocolIsInHTTPFamily()) m_platformRequestUpdated = false; } -void ResourceRequestBase::addHTTPHeaderFields(const HTTPHeaderMap& headerFields) +bool ResourceRequestBase::hasHTTPHeaderField(HTTPHeaderName headerName) const +{ + return m_httpHeaderFields.contains(headerName); +} + +void ResourceRequestBase::setHTTPHeaderFields(HTTPHeaderMap headerFields) { - HTTPHeaderMap::const_iterator end = headerFields.end(); - for (HTTPHeaderMap::const_iterator it = headerFields.begin(); it != end; ++it) - addHTTPHeaderField(it->key, it->value); + updateResourceRequest(); + + m_httpHeaderFields = WTFMove(headerFields); + + if (url().protocolIsInHTTPFamily()) + m_platformRequestUpdated = false; } bool equalIgnoringHeaderFields(const ResourceRequestBase& a, const ResourceRequestBase& b) @@ -440,18 +534,10 @@ bool equalIgnoringHeaderFields(const ResourceRequestBase& a, const ResourceReque if (a.priority() != b.priority()) return false; - FormData* formDataA = a.httpBody(); - FormData* formDataB = b.httpBody(); - - if (!formDataA) - return !formDataB; - if (!formDataB) - return !formDataA; - - if (*formDataA != *formDataB) + if (a.requester() != b.requester()) return false; - - return true; + + return arePointingToEqualData(a.httpBody(), b.httpBody()); } bool ResourceRequestBase::compare(const ResourceRequest& a, const ResourceRequest& b) @@ -465,22 +551,32 @@ bool ResourceRequestBase::compare(const ResourceRequest& a, const ResourceReques return ResourceRequest::platformCompare(a, b); } +static const HTTPHeaderName conditionalHeaderNames[] = { + HTTPHeaderName::IfMatch, + HTTPHeaderName::IfModifiedSince, + HTTPHeaderName::IfNoneMatch, + HTTPHeaderName::IfRange, + HTTPHeaderName::IfUnmodifiedSince +}; + 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")); + updateResourceRequest(); + + for (auto headerName : conditionalHeaderNames) { + if (m_httpHeaderFields.contains(headerName)) + return true; + } + + return false; } void ResourceRequestBase::makeUnconditional() { - m_httpHeaderFields.remove("If-Match"); - m_httpHeaderFields.remove("If-Modified-Since"); - m_httpHeaderFields.remove("If-None-Match"); - m_httpHeaderFields.remove("If-Range"); - m_httpHeaderFields.remove("If-Unmodified-Since"); + updateResourceRequest(); + + for (auto headerName : conditionalHeaderNames) + m_httpHeaderFields.remove(headerName); } double ResourceRequestBase::defaultTimeoutInterval() @@ -523,7 +619,7 @@ void ResourceRequestBase::updateResourceRequest(HTTPBodyUpdatePolicy bodyPolicy) } } -#if !PLATFORM(MAC) && !USE(CFNETWORK) && !USE(SOUP) +#if !PLATFORM(COCOA) && !USE(CFURLCONNECTION) && !USE(SOUP) unsigned initializeMaximumHTTPConnectionCountPerHost() { // This is used by the loader to control the number of issued parallel load requests. @@ -544,4 +640,32 @@ bool ResourceRequestBase::defaultAllowCookies() } #endif +void ResourceRequestBase::setCachePartition(const String& cachePartition) +{ +#if ENABLE(CACHE_PARTITIONING) + ASSERT(!cachePartition.isNull()); + ASSERT(cachePartition == partitionName(cachePartition)); + m_cachePartition = cachePartition; +#else + UNUSED_PARAM(cachePartition); +#endif +} + +String ResourceRequestBase::partitionName(const String& domain) +{ +#if ENABLE(PUBLIC_SUFFIX_LIST) + if (domain.isNull()) + return emptyString(); + String highLevel = topPrivatelyControlledDomain(domain); + if (highLevel.isNull()) + return emptyString(); + return highLevel; +#else +#if ENABLE(CACHE_PARTITIONING) +#error Cache partitioning requires PUBLIC_SUFFIX_LIST +#endif + return emptyString(); +#endif +} + } diff --git a/Source/WebCore/platform/network/ResourceRequestBase.h b/Source/WebCore/platform/network/ResourceRequestBase.h index ea66d6126..86415eaa5 100644 --- a/Source/WebCore/platform/network/ResourceRequestBase.h +++ b/Source/WebCore/platform/network/ResourceRequestBase.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006 Apple Inc. All rights reserved. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * Copyright (C) 2009, 2012 Google Inc. All rights reserved. * @@ -12,17 +12,17 @@ * 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 + * 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 COMPUTER, INC. OR + * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ResourceRequestBase_h @@ -33,217 +33,309 @@ #include "URL.h" #include "ResourceLoadPriority.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 - }; - - enum HTTPBodyUpdatePolicy { - DoNotUpdateHTTPBody, - UpdateHTTPBody - }; - - class ResourceRequest; - struct CrossThreadResourceRequestData; - - // Do not use this type directly. Use ResourceRequest instead. - class ResourceRequestBase { - WTF_MAKE_FAST_ALLOCATED; - public: - 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 URL& url() const; - void setURL(const URL& url); - - void removeCredentials(); - - ResourceRequestCachePolicy cachePolicy() const; - void setCachePolicy(ResourceRequestCachePolicy cachePolicy); - - double timeoutInterval() const; // May return 0 when using platform default. - void setTimeoutInterval(double timeoutInterval); - - const URL& firstPartyForCookies() const; - void setFirstPartyForCookies(const URL& 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); } - void clearHTTPContentType(); - - 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); } - void clearHTTPUserAgent(); - - String httpAccept() const { return httpHeaderField("Accept"); } - void setHTTPAccept(const String& httpAccept) { setHTTPHeaderField("Accept", httpAccept); } - void clearHTTPAccept(); - - const Vector<String>& responseContentDispositionEncodingFallbackArray() const { return m_responseContentDispositionEncodingFallbackArray; } - 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); - - ResourceLoadPriority priority() const; - void setPriority(ResourceLoadPriority); - - bool isConditional() const; - void makeUnconditional(); - - // 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; } - - static double defaultTimeoutInterval(); // May return 0 when using platform default. - static void setDefaultTimeoutInterval(double); +enum ResourceRequestCachePolicy { + UseProtocolCachePolicy, // normal load, equivalent to fetch "default" cache mode. + ReloadIgnoringCacheData, // reload, equivalent to fetch "reload"cache mode. + ReturnCacheDataElseLoad, // back/forward or encoding change - allow stale data, equivalent to fetch "force-cache" cache mode. + ReturnCacheDataDontLoad, // results of a post - allow stale data and only use cache, equivalent to fetch "only-if-cached" cache mode. + DoNotUseAnyCache, // Bypass the cache entirely, equivalent to fetch "no-store" cache mode. + RefreshAnyCacheData, // Serve cache data only if revalidated, equivalent to fetch "no-cache" mode. +}; + +enum HTTPBodyUpdatePolicy { + DoNotUpdateHTTPBody, + UpdateHTTPBody +}; + +class ResourceRequest; + +// Do not use this type directly. Use ResourceRequest instead. +class ResourceRequestBase { + WTF_MAKE_FAST_ALLOCATED; +public: + ResourceRequest isolatedCopy() const; + WEBCORE_EXPORT void setAsIsolatedCopy(const ResourceRequest&); + + WEBCORE_EXPORT bool isNull() const; + WEBCORE_EXPORT bool isEmpty() const; + + WEBCORE_EXPORT const URL& url() const; + WEBCORE_EXPORT void setURL(const URL& url); + + WEBCORE_EXPORT void removeCredentials(); + + WEBCORE_EXPORT ResourceRequestCachePolicy cachePolicy() const; + WEBCORE_EXPORT void setCachePolicy(ResourceRequestCachePolicy cachePolicy); + + double timeoutInterval() const; // May return 0 when using platform default. + void setTimeoutInterval(double timeoutInterval); + + WEBCORE_EXPORT const URL& firstPartyForCookies() const; + WEBCORE_EXPORT void setFirstPartyForCookies(const URL&); + + WEBCORE_EXPORT const String& httpMethod() const; + WEBCORE_EXPORT void setHTTPMethod(const String& httpMethod); + + WEBCORE_EXPORT const HTTPHeaderMap& httpHeaderFields() const; + WEBCORE_EXPORT void setHTTPHeaderFields(HTTPHeaderMap); + + WEBCORE_EXPORT String httpHeaderField(const String& name) const; + WEBCORE_EXPORT String httpHeaderField(HTTPHeaderName) const; + WEBCORE_EXPORT void setHTTPHeaderField(const String& name, const String& value); + WEBCORE_EXPORT void setHTTPHeaderField(HTTPHeaderName, const String& value); + void addHTTPHeaderField(HTTPHeaderName, const String& value); + void addHTTPHeaderField(const String& name, const String& value); + void addHTTPHeaderFieldIfNotPresent(HTTPHeaderName, const String&); + + bool hasHTTPHeaderField(HTTPHeaderName) const; + + // Instead of passing a string literal to any of these functions, just use a HTTPHeaderName instead. + template<size_t length> String httpHeaderField(const char (&)[length]) const = delete; + template<size_t length> void setHTTPHeaderField(const char (&)[length], const String&) = delete; + template<size_t length> void addHTTPHeaderField(const char (&)[length], const String&) = delete; + + WEBCORE_EXPORT void clearHTTPAuthorization(); + + WEBCORE_EXPORT String httpContentType() const; + WEBCORE_EXPORT void setHTTPContentType(const String&); + void clearHTTPContentType(); + + bool hasHTTPHeader(HTTPHeaderName) const; + + WEBCORE_EXPORT String httpReferrer() const; + bool hasHTTPReferrer() const; + WEBCORE_EXPORT void setHTTPReferrer(const String&); + WEBCORE_EXPORT void clearHTTPReferrer(); + + String httpOrigin() const; + bool hasHTTPOrigin() const; + void setHTTPOrigin(const String&); + WEBCORE_EXPORT void clearHTTPOrigin(); + + WEBCORE_EXPORT String httpUserAgent() const; + WEBCORE_EXPORT void setHTTPUserAgent(const String&); + void clearHTTPUserAgent(); + + String httpAccept() const; + void setHTTPAccept(const String&); + void clearHTTPAccept(); + + void clearHTTPAcceptEncoding(); + + const Vector<String>& responseContentDispositionEncodingFallbackArray() const { return m_responseContentDispositionEncodingFallbackArray; } + WEBCORE_EXPORT void setResponseContentDispositionEncodingFallbackArray(const String& encoding1, const String& encoding2 = String(), const String& encoding3 = String()); + + WEBCORE_EXPORT FormData* httpBody() const; + WEBCORE_EXPORT void setHTTPBody(RefPtr<FormData>&&); + + bool allowCookies() const; + void setAllowCookies(bool allowCookies); + + WEBCORE_EXPORT ResourceLoadPriority priority() const; + WEBCORE_EXPORT void setPriority(ResourceLoadPriority); + + WEBCORE_EXPORT static String partitionName(const String& domain); + const String& cachePartition() const { return m_cachePartition; } + WEBCORE_EXPORT void setCachePartition(const String&); + void setDomainForCachePartition(const String& domain) { setCachePartition(partitionName(domain)); } + + WEBCORE_EXPORT bool isConditional() const; + WEBCORE_EXPORT void makeUnconditional(); + + // 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; } + + // Whether this request should be hidden from the Inspector. + bool hiddenFromInspector() const { return m_hiddenFromInspector; } + void setHiddenFromInspector(bool hiddenFromInspector) { m_hiddenFromInspector = hiddenFromInspector; } + + // Whether this request should impact request counting and delay window.onload. + bool ignoreForRequestCount() const { return m_ignoreForRequestCount; } + void setIgnoreForRequestCount(bool ignoreForRequestCount) { m_ignoreForRequestCount = ignoreForRequestCount; } + + enum class Requester { Unspecified, Main, XHR, Fetch, Media }; + Requester requester() const { return m_requester; } + void setRequester(Requester requester) { m_requester = requester; } + + // Who initiated the request so the Inspector can associate it with a context. E.g. a Web Worker. + String initiatorIdentifier() const { return m_initiatorIdentifier; } + void setInitiatorIdentifier(const String& identifier) { m_initiatorIdentifier = identifier; } + +#if !PLATFORM(COCOA) + bool encodingRequiresPlatformData() const { return true; } +#endif + template<class Encoder> void encodeWithoutPlatformData(Encoder&) const; + template<class Decoder> bool decodeWithoutPlatformData(Decoder&); + + WEBCORE_EXPORT static double defaultTimeoutInterval(); // May return 0 when using platform default. + WEBCORE_EXPORT static void setDefaultTimeoutInterval(double); #if PLATFORM(IOS) - static bool defaultAllowCookies(); - static void setDefaultAllowCookies(bool); + WEBCORE_EXPORT static bool defaultAllowCookies(); + WEBCORE_EXPORT static void setDefaultAllowCookies(bool); #endif - 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_resourceRequestBodyUpdated(false) - , m_platformRequestBodyUpdated(true) - , m_reportUploadProgress(false) - , m_reportLoadTiming(false) - , m_reportRawHeaders(false) - , m_priority(ResourceLoadPriorityLow) - { - } - - ResourceRequestBase(const URL& url, ResourceRequestCachePolicy policy) - : m_url(url) - , m_timeoutInterval(s_defaultTimeoutInterval) - , m_httpMethod(ASCIILiteral("GET")) - , m_cachePolicy(policy) + static bool compare(const ResourceRequest&, const ResourceRequest&); + +protected: + // Used when ResourceRequest is initialized from a platform representation of the request + ResourceRequestBase() + : m_platformRequestUpdated(true) + , m_platformRequestBodyUpdated(true) + { + } + + ResourceRequestBase(const URL& url, ResourceRequestCachePolicy policy) + : m_url(url) + , m_timeoutInterval(s_defaultTimeoutInterval) + , m_httpMethod(ASCIILiteral("GET")) + , m_cachePolicy(policy) #if !PLATFORM(IOS) - , m_allowCookies(true) + , m_allowCookies(true) #else - , m_allowCookies(ResourceRequestBase::defaultAllowCookies()) + , m_allowCookies(ResourceRequestBase::defaultAllowCookies()) #endif - , m_resourceRequestUpdated(true) - , m_platformRequestUpdated(false) - , m_resourceRequestBodyUpdated(true) - , m_platformRequestBodyUpdated(false) - , m_reportUploadProgress(false) - , m_reportLoadTiming(false) - , m_reportRawHeaders(false) - , m_priority(ResourceLoadPriorityLow) - { - } - - void updatePlatformRequest(HTTPBodyUpdatePolicy = DoNotUpdateHTTPBody) const; - void updateResourceRequest(HTTPBodyUpdatePolicy = DoNotUpdateHTTPBody) const; - - // The ResourceRequest subclass may "shadow" this method to compare platform specific fields - static bool platformCompare(const ResourceRequest&, const ResourceRequest&) { return true; } - - URL m_url; - double m_timeoutInterval; // 0 is a magic value for platform default on platforms that have one. - URL m_firstPartyForCookies; - String m_httpMethod; - HTTPHeaderMap m_httpHeaderFields; - Vector<String> m_responseContentDispositionEncodingFallbackArray; - RefPtr<FormData> m_httpBody; - ResourceRequestCachePolicy m_cachePolicy : 3; - bool m_allowCookies : 1; - mutable bool m_resourceRequestUpdated : 1; - mutable bool m_platformRequestUpdated : 1; - mutable bool m_resourceRequestBodyUpdated : 1; - mutable bool m_platformRequestBodyUpdated : 1; - bool m_reportUploadProgress : 1; - bool m_reportLoadTiming : 1; - bool m_reportRawHeaders : 1; - ResourceLoadPriority m_priority : 4; - - private: - const ResourceRequest& asResourceRequest() const; - - static double s_defaultTimeoutInterval; + , m_resourceRequestUpdated(true) + , m_resourceRequestBodyUpdated(true) + { + } + + void updatePlatformRequest(HTTPBodyUpdatePolicy = DoNotUpdateHTTPBody) const; + void updateResourceRequest(HTTPBodyUpdatePolicy = DoNotUpdateHTTPBody) const; + + template<class Encoder> void encodeBase(Encoder&) const; + template<class Decoder> bool decodeBase(Decoder&); + + // The ResourceRequest subclass may "shadow" this method to compare platform specific fields + static bool platformCompare(const ResourceRequest&, const ResourceRequest&) { return true; } + + URL m_url; + double m_timeoutInterval; // 0 is a magic value for platform default on platforms that have one. + URL m_firstPartyForCookies; + String m_httpMethod; + HTTPHeaderMap m_httpHeaderFields; + Vector<String> m_responseContentDispositionEncodingFallbackArray; + RefPtr<FormData> m_httpBody; + ResourceRequestCachePolicy m_cachePolicy { UseProtocolCachePolicy }; + bool m_allowCookies { false }; + mutable bool m_resourceRequestUpdated { false }; + mutable bool m_platformRequestUpdated { false }; + mutable bool m_resourceRequestBodyUpdated { false }; + mutable bool m_platformRequestBodyUpdated { false }; + bool m_reportUploadProgress { false }; + bool m_reportLoadTiming { false }; + bool m_reportRawHeaders { false }; + bool m_hiddenFromInspector { false }; + bool m_ignoreForRequestCount { false }; + ResourceLoadPriority m_priority { ResourceLoadPriority::Low }; + Requester m_requester { Requester::Unspecified }; + String m_initiatorIdentifier; + String m_cachePartition { emptyString() }; + +private: + const ResourceRequest& asResourceRequest() const; + + WEBCORE_EXPORT static double s_defaultTimeoutInterval; #if PLATFORM(IOS) - static bool s_defaultAllowCookies; + static bool s_defaultAllowCookies; #endif - }; - - 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 { - WTF_MAKE_NONCOPYABLE(CrossThreadResourceRequestDataBase); WTF_MAKE_FAST_ALLOCATED; - public: - CrossThreadResourceRequestDataBase() { } - URL m_url; - - ResourceRequestCachePolicy m_cachePolicy; - double m_timeoutInterval; - URL m_firstPartyForCookies; - - String m_httpMethod; - OwnPtr<CrossThreadHTTPHeaderMapData> m_httpHeaders; - Vector<String> m_responseContentDispositionEncodingFallbackArray; - RefPtr<FormData> m_httpBody; - bool m_allowCookies; - ResourceLoadPriority m_priority; - }; - - unsigned initializeMaximumHTTPConnectionCountPerHost(); +}; + +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); } + +WEBCORE_EXPORT unsigned initializeMaximumHTTPConnectionCountPerHost(); #if PLATFORM(IOS) - void initializeHTTPConnectionSettingsOnStartup(); +WEBCORE_EXPORT void initializeHTTPConnectionSettingsOnStartup(); #endif + +template<class Encoder> +ALWAYS_INLINE void ResourceRequestBase::encodeBase(Encoder& encoder) const +{ + encoder << m_url; + encoder << m_timeoutInterval; + encoder << m_firstPartyForCookies.string(); + encoder << m_httpMethod; + encoder << m_httpHeaderFields; + encoder << m_responseContentDispositionEncodingFallbackArray; + encoder.encodeEnum(m_cachePolicy); + encoder << m_allowCookies; + encoder.encodeEnum(m_priority); + encoder.encodeEnum(m_requester); +} + +template<class Decoder> +ALWAYS_INLINE bool ResourceRequestBase::decodeBase(Decoder& decoder) +{ + if (!decoder.decode(m_url)) + return false; + + if (!decoder.decode(m_timeoutInterval)) + return false; + + String firstPartyForCookies; + if (!decoder.decode(firstPartyForCookies)) + return false; + m_firstPartyForCookies = URL(ParsedURLString, firstPartyForCookies); + + if (!decoder.decode(m_httpMethod)) + return false; + + if (!decoder.decode(m_httpHeaderFields)) + return false; + + if (!decoder.decode(m_responseContentDispositionEncodingFallbackArray)) + return false; + + ResourceRequestCachePolicy cachePolicy; + if (!decoder.decodeEnum(cachePolicy)) + return false; + m_cachePolicy = cachePolicy; + + bool allowCookies; + if (!decoder.decode(allowCookies)) + return false; + m_allowCookies = allowCookies; + + ResourceLoadPriority priority; + if (!decoder.decodeEnum(priority)) + return false; + m_priority = priority; + + if (!decoder.decodeEnum(m_requester)) + return false; + + return true; +} + +template<class Encoder> +void ResourceRequestBase::encodeWithoutPlatformData(Encoder& encoder) const +{ + ASSERT(!m_httpBody); + ASSERT(!m_platformRequestUpdated); + encodeBase(encoder); +} + +template<class Decoder> +bool ResourceRequestBase::decodeWithoutPlatformData(Decoder& decoder) +{ + return decodeBase(decoder); +} + } // namespace WebCore #endif // ResourceRequestBase_h diff --git a/Source/WebCore/platform/network/ResourceResponseBase.cpp b/Source/WebCore/platform/network/ResourceResponseBase.cpp index 5c057ebb4..fd76f6b34 100644 --- a/Source/WebCore/platform/network/ResourceResponseBase.cpp +++ b/Source/WebCore/platform/network/ResourceResponseBase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2008, 2016 Apple Inc. All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -27,120 +27,125 @@ #include "config.h" #include "ResourceResponseBase.h" +#include "CacheValidation.h" +#include "HTTPHeaderNames.h" #include "HTTPParsers.h" +#include "ParsedContentRange.h" #include "ResourceResponse.h" #include <wtf/CurrentTime.h> #include <wtf/MathExtras.h> #include <wtf/StdLibExtras.h> +#include <wtf/text/StringView.h> namespace WebCore { -static void parseCacheHeader(const String& header, Vector<std::pair<String, String>>& result); - -inline const ResourceResponse& ResourceResponseBase::asResourceResponse() const +ResourceResponseBase::ResourceResponseBase() + : m_isNull(true) + , m_expectedContentLength(0) + , m_httpStatusCode(0) { - return *static_cast<const ResourceResponse*>(this); } -ResourceResponseBase::ResourceResponseBase() - : m_expectedContentLength(0) - , m_httpStatusCode(0) - , m_connectionID(0) - , m_cacheControlMaxAge(0) - , m_age(0) - , m_date(0) - , m_expires(0) - , m_lastModified(0) - , m_wasCached(false) - , 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) -{ -} - -ResourceResponseBase::ResourceResponseBase(const URL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) - : m_url(url) +ResourceResponseBase::ResourceResponseBase(const URL& url, const String& mimeType, long long expectedLength, const String& textEncodingName) + : m_isNull(false) + , m_url(url) , m_mimeType(mimeType) , m_expectedContentLength(expectedLength) , m_textEncodingName(textEncodingName) - , m_suggestedFilename(filename) + , m_certificateInfo(CertificateInfo()) // Empty but valid for synthetic responses. , m_httpStatusCode(0) - , m_connectionID(0) - , m_cacheControlMaxAge(0) - , m_age(0) - , m_date(0) - , m_expires(0) - , m_lastModified(0) - , m_wasCached(false) - , 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) -{ -} - -PassOwnPtr<ResourceResponse> ResourceResponseBase::adopt(PassOwnPtr<CrossThreadResourceResponseData> data) -{ - OwnPtr<ResourceResponse> response = adoptPtr(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(CommonAndUncommonFields); - response->m_httpHeaderFields.adopt(data->m_httpHeaders.release()); - response->setResourceLoadTiming(data->m_resourceLoadTiming.release()); - response->doPlatformAdopt(data); - return response.release(); -} - -PassOwnPtr<CrossThreadResourceResponseData> ResourceResponseBase::copyData() const -{ - OwnPtr<CrossThreadResourceResponseData> data = adoptPtr(new CrossThreadResourceResponseData); - data->m_url = url().copy(); - data->m_mimeType = mimeType().isolatedCopy(); - data->m_expectedContentLength = expectedContentLength(); - data->m_textEncodingName = textEncodingName().isolatedCopy(); - data->m_suggestedFilename = suggestedFilename().isolatedCopy(); - data->m_httpStatusCode = httpStatusCode(); - data->m_httpStatusText = httpStatusText().isolatedCopy(); - data->m_httpHeaders = httpHeaderFields().copyData(); - if (m_resourceLoadTiming) - data->m_resourceLoadTiming = m_resourceLoadTiming->deepCopy(); - return asResourceResponse().doPlatformCopyData(data.release()); +{ } +ResourceResponseBase::CrossThreadData ResourceResponseBase::crossThreadData() const +{ + CrossThreadData data; + + data.url = url().isolatedCopy(); + data.mimeType = mimeType().isolatedCopy(); + data.expectedContentLength = expectedContentLength(); + data.textEncodingName = textEncodingName().isolatedCopy(); + + data.httpStatusCode = httpStatusCode(); + data.httpStatusText = httpStatusText().isolatedCopy(); + data.httpVersion = httpVersion().isolatedCopy(); + + data.httpHeaderFields = httpHeaderFields().isolatedCopy(); + data.networkLoadTiming = m_networkLoadTiming.isolatedCopy(); + data.type = m_type; + data.isRedirected = m_isRedirected; + + return data; +} + +ResourceResponse ResourceResponseBase::fromCrossThreadData(CrossThreadData&& data) +{ + ResourceResponse response; + + response.setURL(data.url); + response.setMimeType(data.mimeType); + response.setExpectedContentLength(data.expectedContentLength); + response.setTextEncodingName(data.textEncodingName); + + response.setHTTPStatusCode(data.httpStatusCode); + response.setHTTPStatusText(data.httpStatusText); + response.setHTTPVersion(data.httpVersion); + + response.m_httpHeaderFields = WTFMove(data.httpHeaderFields); + response.m_networkLoadTiming = data.networkLoadTiming; + response.m_type = data.type; + response.m_isRedirected = data.isRedirected; + + return response; +} + +ResourceResponse ResourceResponseBase::filterResponse(const ResourceResponse& response, ResourceResponse::Tainting tainting) +{ + if (tainting == ResourceResponse::Tainting::Opaque) { + ResourceResponse opaqueResponse; + opaqueResponse.setType(ResourceResponse::Type::Opaque); + return opaqueResponse; + } + + ResourceResponse filteredResponse = response; + // Let's initialize filteredResponse to remove some header fields. + filteredResponse.lazyInit(AllFields); + + if (tainting == ResourceResponse::Tainting::Basic) { + filteredResponse.setType(ResourceResponse::Type::Basic); + filteredResponse.m_httpHeaderFields.remove(HTTPHeaderName::SetCookie); + filteredResponse.m_httpHeaderFields.remove(HTTPHeaderName::SetCookie2); + return filteredResponse; + } + + ASSERT(tainting == ResourceResponse::Tainting::Cors); + filteredResponse.setType(ResourceResponse::Type::Cors); + + HTTPHeaderSet accessControlExposeHeaderSet; + parseAccessControlExposeHeadersAllowList(response.httpHeaderField(HTTPHeaderName::AccessControlExposeHeaders), accessControlExposeHeaderSet); + filteredResponse.m_httpHeaderFields.uncommonHeaders().removeIf([&](auto& entry) { + return !isCrossOriginSafeHeader(entry.key, accessControlExposeHeaderSet); + }); + filteredResponse.m_httpHeaderFields.commonHeaders().removeIf([&](auto& entry) { + return !isCrossOriginSafeHeader(entry.key, accessControlExposeHeaderSet); + }); + + return filteredResponse; +} + +// FIXME: Name does not make it clear this is true for HTTPS! bool ResourceResponseBase::isHTTP() const { lazyInit(CommonFieldsOnly); - String protocol = m_url.protocol(); - - return equalIgnoringCase(protocol, "http") || equalIgnoringCase(protocol, "https"); + return m_url.protocolIsInHTTPFamily(); } const URL& ResourceResponseBase::url() const { lazyInit(CommonFieldsOnly); - return m_url; + return m_url; } void ResourceResponseBase::setURL(const URL& url) @@ -207,23 +212,22 @@ void ResourceResponseBase::setTextEncodingName(const String& encodingName) // FIXME: Should invalidate or update platform response if present. } -// FIXME should compute this on the fly -const String& ResourceResponseBase::suggestedFilename() const +void ResourceResponseBase::includeCertificateInfo() const { - lazyInit(AllFields); - - return m_suggestedFilename; + if (m_certificateInfo) + return; + m_certificateInfo = static_cast<const ResourceResponse*>(this)->platformCertificateInfo(); } -void ResourceResponseBase::setSuggestedFilename(const String& suggestedName) +String ResourceResponseBase::suggestedFilename() const { - lazyInit(AllFields); - m_isNull = false; - - // FIXME: Suggested file name is calculated based on other headers. There should not be a setter for it. - m_suggestedFilename = suggestedName; + return static_cast<const ResourceResponse*>(this)->platformSuggestedFilename(); +} - // FIXME: Should invalidate or update platform response if present. +bool ResourceResponseBase::isSuccessful() const +{ + int code = httpStatusCode(); + return code >= 200 && code < 300; } int ResourceResponseBase::httpStatusCode() const @@ -244,21 +248,44 @@ void ResourceResponseBase::setHTTPStatusCode(int statusCode) const String& ResourceResponseBase::httpStatusText() const { - lazyInit(CommonAndUncommonFields); + lazyInit(AllFields); return m_httpStatusText; } void ResourceResponseBase::setHTTPStatusText(const String& statusText) { - lazyInit(CommonAndUncommonFields); + lazyInit(AllFields); m_httpStatusText = statusText; // FIXME: Should invalidate or update platform response if present. } -String ResourceResponseBase::httpHeaderField(const AtomicString& name) const +const String& ResourceResponseBase::httpVersion() const +{ + lazyInit(AllFields); + + return m_httpVersion; +} + +void ResourceResponseBase::setHTTPVersion(const String& versionText) +{ + lazyInit(AllFields); + + m_httpVersion = versionText; + + // FIXME: Should invalidate or update platform response if present. +} + +bool ResourceResponseBase::isHTTP09() const +{ + lazyInit(AllFields); + + return m_httpVersion.startsWith("HTTP/0.9"); +} + +String ResourceResponseBase::httpHeaderField(const String& name) const { lazyInit(CommonFieldsOnly); @@ -267,12 +294,12 @@ String ResourceResponseBase::httpHeaderField(const AtomicString& name) const if (!value.isEmpty()) return value; - lazyInit(CommonAndUncommonFields); + lazyInit(AllFields); return m_httpHeaderFields.get(name); } -String ResourceResponseBase::httpHeaderField(const char* name) const +String ResourceResponseBase::httpHeaderField(HTTPHeaderName name) const { lazyInit(CommonFieldsOnly); @@ -281,57 +308,89 @@ String ResourceResponseBase::httpHeaderField(const char* name) const if (!value.isEmpty()) return value; - lazyInit(CommonAndUncommonFields); + lazyInit(AllFields); return m_httpHeaderFields.get(name); } -void ResourceResponseBase::updateHeaderParsedState(const AtomicString& name) +void ResourceResponseBase::updateHeaderParsedState(HTTPHeaderName name) { - DEFINE_STATIC_LOCAL(const AtomicString, ageHeader, ("age", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, cacheControlHeader, ("cache-control", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, dateHeader, ("date", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, expiresHeader, ("expires", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, lastModifiedHeader, ("last-modified", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, pragmaHeader, ("pragma", AtomicString::ConstructFromLiteral)); - - if (equalIgnoringCase(name, ageHeader)) + switch (name) { + case HTTPHeaderName::Age: m_haveParsedAgeHeader = false; - else if (equalIgnoringCase(name, cacheControlHeader) || equalIgnoringCase(name, pragmaHeader)) + break; + + case HTTPHeaderName::CacheControl: + case HTTPHeaderName::Pragma: m_haveParsedCacheControlHeader = false; - else if (equalIgnoringCase(name, dateHeader)) + break; + + case HTTPHeaderName::Date: m_haveParsedDateHeader = false; - else if (equalIgnoringCase(name, expiresHeader)) + break; + + case HTTPHeaderName::Expires: m_haveParsedExpiresHeader = false; - else if (equalIgnoringCase(name, lastModifiedHeader)) + break; + + case HTTPHeaderName::LastModified: m_haveParsedLastModifiedHeader = false; + break; + + case HTTPHeaderName::ContentRange: + m_haveParsedContentRangeHeader = false; + break; + + default: + break; + } } -void ResourceResponseBase::setHTTPHeaderField(const AtomicString& name, const String& value) +void ResourceResponseBase::setHTTPHeaderField(const String& name, const String& value) { - lazyInit(CommonAndUncommonFields); + lazyInit(AllFields); - updateHeaderParsedState(name); + HTTPHeaderName headerName; + if (findHTTPHeaderName(name, headerName)) + updateHeaderParsedState(headerName); m_httpHeaderFields.set(name, value); // FIXME: Should invalidate or update platform response if present. } -void ResourceResponseBase::addHTTPHeaderField(const AtomicString& name, const String& value) +void ResourceResponseBase::setHTTPHeaderField(HTTPHeaderName name, const String& value) { - lazyInit(CommonAndUncommonFields); + lazyInit(AllFields); updateHeaderParsedState(name); - HTTPHeaderMap::AddResult result = m_httpHeaderFields.add(name, value); - if (!result.isNewEntry) - result.iterator->value.append(", " + value); + m_httpHeaderFields.set(name, value); + + // FIXME: Should invalidate or update platform response if present. +} + +void ResourceResponseBase::addHTTPHeaderField(HTTPHeaderName name, const String& value) +{ + lazyInit(AllFields); + updateHeaderParsedState(name); + m_httpHeaderFields.add(name, value); +} + +void ResourceResponseBase::addHTTPHeaderField(const String& name, const String& value) +{ + HTTPHeaderName headerName; + if (findHTTPHeaderName(name, headerName)) + addHTTPHeaderField(headerName, value); + else { + lazyInit(AllFields); + m_httpHeaderFields.add(name, value); + } } const HTTPHeaderMap& ResourceResponseBase::httpHeaderFields() const { - lazyInit(CommonAndUncommonFields); + lazyInit(AllFields); return m_httpHeaderFields; } @@ -342,227 +401,175 @@ void ResourceResponseBase::parseCacheControlDirectives() const lazyInit(CommonFieldsOnly); + m_cacheControlDirectives = WebCore::parseCacheControlDirectives(m_httpHeaderFields); m_haveParsedCacheControlHeader = true; - - m_cacheControlContainsMustRevalidate = false; - m_cacheControlContainsNoCache = false; - m_cacheControlMaxAge = std::numeric_limits<double>::quiet_NaN(); - - DEFINE_STATIC_LOCAL(const AtomicString, cacheControlString, ("cache-control", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, noCacheDirective, ("no-cache", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, noStoreDirective, ("no-store", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, mustRevalidateDirective, ("must-revalidate", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, maxAgeDirective, ("max-age", AtomicString::ConstructFromLiteral)); - - String cacheControlValue = m_httpHeaderFields.get(cacheControlString); - if (!cacheControlValue.isEmpty()) { - Vector<std::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)) { - if (!std::isnan(m_cacheControlMaxAge)) { - // First max-age directive wins if there are multiple ones. - continue; - } - 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", AtomicString::ConstructFromLiteral)); - String pragmaValue = m_httpHeaderFields.get(pragmaHeader); - - m_cacheControlContainsNoCache = pragmaValue.lower().contains(noCacheDirective); - } } bool ResourceResponseBase::cacheControlContainsNoCache() const { if (!m_haveParsedCacheControlHeader) parseCacheControlDirectives(); - return m_cacheControlContainsNoCache; + return m_cacheControlDirectives.noCache; } bool ResourceResponseBase::cacheControlContainsNoStore() const { if (!m_haveParsedCacheControlHeader) parseCacheControlDirectives(); - return m_cacheControlContainsNoStore; + return m_cacheControlDirectives.noStore; } bool ResourceResponseBase::cacheControlContainsMustRevalidate() const { if (!m_haveParsedCacheControlHeader) parseCacheControlDirectives(); - return m_cacheControlContainsMustRevalidate; + return m_cacheControlDirectives.mustRevalidate; +} + +bool ResourceResponseBase::cacheControlContainsImmutable() const +{ + if (!m_haveParsedCacheControlHeader) + parseCacheControlDirectives(); + return m_cacheControlDirectives.immutable; } bool ResourceResponseBase::hasCacheValidatorFields() const { lazyInit(CommonFieldsOnly); - DEFINE_STATIC_LOCAL(const AtomicString, lastModifiedHeader, ("last-modified", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(const AtomicString, eTagHeader, ("etag", AtomicString::ConstructFromLiteral)); - return !m_httpHeaderFields.get(lastModifiedHeader).isEmpty() || !m_httpHeaderFields.get(eTagHeader).isEmpty(); + return !m_httpHeaderFields.get(HTTPHeaderName::LastModified).isEmpty() || !m_httpHeaderFields.get(HTTPHeaderName::ETag).isEmpty(); } -double ResourceResponseBase::cacheControlMaxAge() const +std::optional<std::chrono::microseconds> ResourceResponseBase::cacheControlMaxAge() const { if (!m_haveParsedCacheControlHeader) parseCacheControlDirectives(); - return m_cacheControlMaxAge; + return m_cacheControlDirectives.maxAge; } -static double parseDateValueInHeader(const HTTPHeaderMap& headers, const AtomicString& headerName) +static std::optional<std::chrono::system_clock::time_point> parseDateValueInHeader(const HTTPHeaderMap& headers, HTTPHeaderName headerName) { String headerValue = headers.get(headerName); if (headerValue.isEmpty()) - return std::numeric_limits<double>::quiet_NaN(); + return { }; // 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 (!std::isfinite(dateInMilliseconds)) - return std::numeric_limits<double>::quiet_NaN(); - return dateInMilliseconds / 1000; + return parseHTTPDate(headerValue); } -double ResourceResponseBase::date() const +std::optional<std::chrono::system_clock::time_point> ResourceResponseBase::date() const { lazyInit(CommonFieldsOnly); if (!m_haveParsedDateHeader) { - DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("date", AtomicString::ConstructFromLiteral)); - m_date = parseDateValueInHeader(m_httpHeaderFields, headerName); + m_date = parseDateValueInHeader(m_httpHeaderFields, HTTPHeaderName::Date); m_haveParsedDateHeader = true; } return m_date; } -double ResourceResponseBase::age() const +std::optional<std::chrono::microseconds> ResourceResponseBase::age() const { + using namespace std::chrono; + lazyInit(CommonFieldsOnly); if (!m_haveParsedAgeHeader) { - DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("age", AtomicString::ConstructFromLiteral)); - String headerValue = m_httpHeaderFields.get(headerName); + String headerValue = m_httpHeaderFields.get(HTTPHeaderName::Age); bool ok; - m_age = headerValue.toDouble(&ok); - if (!ok) - m_age = std::numeric_limits<double>::quiet_NaN(); + double ageDouble = headerValue.toDouble(&ok); + if (ok) + m_age = duration_cast<microseconds>(duration<double>(ageDouble)); m_haveParsedAgeHeader = true; } return m_age; } -double ResourceResponseBase::expires() const +std::optional<std::chrono::system_clock::time_point> ResourceResponseBase::expires() const { lazyInit(CommonFieldsOnly); if (!m_haveParsedExpiresHeader) { - DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("expires", AtomicString::ConstructFromLiteral)); - m_expires = parseDateValueInHeader(m_httpHeaderFields, headerName); + m_expires = parseDateValueInHeader(m_httpHeaderFields, HTTPHeaderName::Expires); m_haveParsedExpiresHeader = true; } return m_expires; } -double ResourceResponseBase::lastModified() const +std::optional<std::chrono::system_clock::time_point> ResourceResponseBase::lastModified() const { lazyInit(CommonFieldsOnly); if (!m_haveParsedLastModifiedHeader) { - DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("last-modified", AtomicString::ConstructFromLiteral)); - m_lastModified = parseDateValueInHeader(m_httpHeaderFields, headerName); + m_lastModified = parseDateValueInHeader(m_httpHeaderFields, HTTPHeaderName::LastModified); +#if PLATFORM(COCOA) + // CFNetwork converts malformed dates into Epoch so we need to treat Epoch as + // an invalid value (rdar://problem/22352838). + const std::chrono::system_clock::time_point epoch; + if (m_lastModified && m_lastModified.value() == epoch) + m_lastModified = std::nullopt; +#endif m_haveParsedLastModifiedHeader = true; } return m_lastModified; } -bool ResourceResponseBase::isAttachment() const +static ParsedContentRange parseContentRangeInHeader(const HTTPHeaderMap& headers) { - lazyInit(CommonAndUncommonFields); + String contentRangeValue = headers.get(HTTPHeaderName::ContentRange); + if (contentRangeValue.isEmpty()) + return ParsedContentRange(); - DEFINE_STATIC_LOCAL(const AtomicString, headerName, ("content-disposition", AtomicString::ConstructFromLiteral)); - 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", AtomicString::ConstructFromLiteral)); - return equalIgnoringCase(value, attachmentString); + return ParsedContentRange(contentRangeValue); } - -bool ResourceResponseBase::wasCached() const -{ - lazyInit(CommonAndUncommonFields); - return m_wasCached; -} - -void ResourceResponseBase::setWasCached(bool value) +ParsedContentRange& ResourceResponseBase::contentRange() const { - m_wasCached = value; -} + lazyInit(CommonFieldsOnly); -bool ResourceResponseBase::connectionReused() const -{ - lazyInit(CommonAndUncommonFields); + if (!m_haveParsedContentRangeHeader) { + m_contentRange = parseContentRangeInHeader(m_httpHeaderFields); + m_haveParsedContentRangeHeader = true; + } - return m_connectionReused; + return m_contentRange; } -void ResourceResponseBase::setConnectionReused(bool connectionReused) +bool ResourceResponseBase::isAttachment() const { - lazyInit(CommonAndUncommonFields); + lazyInit(AllFields); - m_connectionReused = connectionReused; + auto value = m_httpHeaderFields.get(HTTPHeaderName::ContentDisposition); + return equalLettersIgnoringASCIICase(value.left(value.find(';')).stripWhiteSpace(), "attachment"); } -unsigned ResourceResponseBase::connectionID() const +bool ResourceResponseBase::isAttachmentWithFilename() const { - lazyInit(CommonAndUncommonFields); + lazyInit(AllFields); - return m_connectionID; -} + String contentDisposition = m_httpHeaderFields.get(HTTPHeaderName::ContentDisposition); + if (contentDisposition.isNull()) + return false; -void ResourceResponseBase::setConnectionID(unsigned connectionID) -{ - lazyInit(CommonAndUncommonFields); + if (!equalLettersIgnoringASCIICase(contentDisposition.left(contentDisposition.find(';')).stripWhiteSpace(), "attachment")) + return false; - m_connectionID = connectionID; + String filename = filenameFromHTTPContentDisposition(contentDisposition); + return !filename.isNull(); } -ResourceLoadTiming* ResourceResponseBase::resourceLoadTiming() const +ResourceResponseBase::Source ResourceResponseBase::source() const { - lazyInit(CommonAndUncommonFields); + lazyInit(AllFields); - return m_resourceLoadTiming.get(); + return m_source; } -void ResourceResponseBase::setResourceLoadTiming(PassRefPtr<ResourceLoadTiming> resourceLoadTiming) +void ResourceResponseBase::setSource(Source source) { - lazyInit(CommonAndUncommonFields); - - m_resourceLoadTiming = resourceLoadTiming; + m_source = source; } void ResourceResponseBase::lazyInit(InitLevel initLevel) const @@ -590,106 +597,9 @@ bool ResourceResponseBase::compare(const ResourceResponse& a, const ResourceResp 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()) + if (a.networkLoadTiming() != b.networkLoadTiming()) 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)); -} - -static void parseCacheHeader(const String& header, Vector<std::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(std::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(std::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(','); - if (nextCommaPosition2 != notFound) { - // The value is delimited by the next comma - result.append(std::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(std::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(std::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(std::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 index 9d838acc4..4365d68b9 100644 --- a/Source/WebCore/platform/network/ResourceResponseBase.h +++ b/Source/WebCore/platform/network/ResourceResponseBase.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2008, 2016 Apple Inc. All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -24,93 +24,123 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceResponseBase_h -#define ResourceResponseBase_h +#pragma once +#include "CacheValidation.h" +#include "CertificateInfo.h" #include "HTTPHeaderMap.h" +#include "NetworkLoadTiming.h" +#include "ParsedContentRange.h" #include "URL.h" -#include "ResourceLoadTiming.h" - -#include <wtf/PassOwnPtr.h> -#include <wtf/RefPtr.h> - -#if OS(SOLARIS) -#include <sys/time.h> // For time_t structure. -#endif +#include <wtf/SHA1.h> namespace WebCore { class ResourceResponse; -struct CrossThreadResourceResponseData; // Do not use this class directly, use the class ResponseResponse instead class ResourceResponseBase { WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<ResourceResponse> adopt(PassOwnPtr<CrossThreadResourceResponseData>); + enum class Type; + + struct CrossThreadData { + CrossThreadData(const CrossThreadData&) = delete; + CrossThreadData& operator=(const CrossThreadData&) = delete; + CrossThreadData() = default; + CrossThreadData(CrossThreadData&&) = default; + + URL url; + String mimeType; + long long expectedContentLength; + String textEncodingName; + int httpStatusCode; + String httpStatusText; + String httpVersion; + HTTPHeaderMap httpHeaderFields; + NetworkLoadTiming networkLoadTiming; + Type type; + bool isRedirected; + }; - // Gets a copy of the data suitable for passing to another thread. - PassOwnPtr<CrossThreadResourceResponseData> copyData() const; + CrossThreadData crossThreadData() const; + static ResourceResponse fromCrossThreadData(CrossThreadData&&); + + enum class Tainting { Basic, Cors, Opaque }; + static ResourceResponse filterResponse(const ResourceResponse&, Tainting); bool isNull() const { return m_isNull; } - bool isHTTP() const; + WEBCORE_EXPORT bool isHTTP() const; + bool isSuccessful() const; - const URL& url() const; - void setURL(const URL& url); + WEBCORE_EXPORT const URL& url() const; + WEBCORE_EXPORT void setURL(const URL&); - const String& mimeType() const; - void setMimeType(const String& mimeType); + WEBCORE_EXPORT const String& mimeType() const; + WEBCORE_EXPORT void setMimeType(const String& mimeType); - long long expectedContentLength() const; - void setExpectedContentLength(long long expectedContentLength); + WEBCORE_EXPORT long long expectedContentLength() const; + WEBCORE_EXPORT void setExpectedContentLength(long long expectedContentLength); - const String& textEncodingName() const; - void setTextEncodingName(const String& name); + WEBCORE_EXPORT const String& textEncodingName() const; + WEBCORE_EXPORT void setTextEncodingName(const String& name); - // FIXME: Should compute this on the fly. - // There should not be a setter exposed, as suggested file name is determined based on other headers in a manner that WebCore does not necessarily know about. - const String& suggestedFilename() const; - void setSuggestedFilename(const String&); + WEBCORE_EXPORT int httpStatusCode() const; + WEBCORE_EXPORT void setHTTPStatusCode(int); - 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); - void addHTTPHeaderField(const AtomicString& name, const String& value); - const HTTPHeaderMap& httpHeaderFields() const; + WEBCORE_EXPORT const String& httpStatusText() const; + WEBCORE_EXPORT void setHTTPStatusText(const String&); - bool isMultipart() const { return mimeType() == "multipart/x-mixed-replace"; } + WEBCORE_EXPORT const String& httpVersion() const; + WEBCORE_EXPORT void setHTTPVersion(const String&); + WEBCORE_EXPORT bool isHTTP09() const; - bool isAttachment() 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; - bool hasCacheValidatorFields() const; - double cacheControlMaxAge() const; - double date() const; - double age() const; - double expires() const; - double lastModified() const; + WEBCORE_EXPORT const HTTPHeaderMap& httpHeaderFields() const; - unsigned connectionID() const; - void setConnectionID(unsigned); + String httpHeaderField(const String& name) const; + WEBCORE_EXPORT String httpHeaderField(HTTPHeaderName) const; + WEBCORE_EXPORT void setHTTPHeaderField(const String& name, const String& value); + WEBCORE_EXPORT void setHTTPHeaderField(HTTPHeaderName, const String& value); - bool connectionReused() const; - void setConnectionReused(bool); + void addHTTPHeaderField(HTTPHeaderName, const String& value); + void addHTTPHeaderField(const String& name, const String& value); - bool wasCached() const; - void setWasCached(bool); + // Instead of passing a string literal to any of these functions, just use a HTTPHeaderName instead. + template<size_t length> String httpHeaderField(const char (&)[length]) const = delete; + template<size_t length> void setHTTPHeaderField(const char (&)[length], const String&) = delete; + template<size_t length> void addHTTPHeaderField(const char (&)[length], const String&) = delete; - ResourceLoadTiming* resourceLoadTiming() const; - void setResourceLoadTiming(PassRefPtr<ResourceLoadTiming>); + bool isMultipart() const { return mimeType() == "multipart/x-mixed-replace"; } + + WEBCORE_EXPORT bool isAttachment() const; + WEBCORE_EXPORT bool isAttachmentWithFilename() const; + WEBCORE_EXPORT String suggestedFilename() const; + + WEBCORE_EXPORT void includeCertificateInfo() const; + const std::optional<CertificateInfo>& certificateInfo() const { return m_certificateInfo; }; + + // These functions return parsed values of the corresponding response headers. + WEBCORE_EXPORT bool cacheControlContainsNoCache() const; + WEBCORE_EXPORT bool cacheControlContainsNoStore() const; + WEBCORE_EXPORT bool cacheControlContainsMustRevalidate() const; + WEBCORE_EXPORT bool cacheControlContainsImmutable() const; + WEBCORE_EXPORT bool hasCacheValidatorFields() const; + WEBCORE_EXPORT std::optional<std::chrono::microseconds> cacheControlMaxAge() const; + WEBCORE_EXPORT std::optional<std::chrono::system_clock::time_point> date() const; + WEBCORE_EXPORT std::optional<std::chrono::microseconds> age() const; + WEBCORE_EXPORT std::optional<std::chrono::system_clock::time_point> expires() const; + WEBCORE_EXPORT std::optional<std::chrono::system_clock::time_point> lastModified() const; + ParsedContentRange& contentRange() const; + + // This is primarily for testing support. It is not necessarily accurate in all scenarios. + enum class Source { Unknown, Network, DiskCache, DiskCacheAfterValidation, MemoryCache, MemoryCacheAfterValidation }; + WEBCORE_EXPORT Source source() const; + WEBCORE_EXPORT void setSource(Source); + + const std::optional<SHA1::Digest>& cacheBodyKey() const { return m_cacheBodyKey; } + void setCacheBodyKey(const SHA1::Digest& key) { m_cacheBodyKey = key; } + + NetworkLoadTiming& networkLoadTiming() const { return m_networkLoadTiming; } // The ResourceResponse subclass may "shadow" this method to provide platform-specific memory usage information unsigned memoryUsage() const @@ -119,86 +149,154 @@ public: return 1280; } + enum class Type { Basic, Cors, Default, Error, Opaque, Opaqueredirect }; + Type type() const { return m_type; } + void setType(Type type) { m_type = type; } + bool isRedirected() const { return m_isRedirected; } + void setRedirected(bool isRedirected) { m_isRedirected = isRedirected; } + static bool compare(const ResourceResponse&, const ResourceResponse&); + template<class Encoder> void encode(Encoder&) const; + template<class Decoder> static bool decode(Decoder&, ResourceResponseBase&); + protected: enum InitLevel { Uninitialized, CommonFieldsOnly, - CommonAndUncommonFields, AllFields }; - ResourceResponseBase(); - ResourceResponseBase(const URL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename); + WEBCORE_EXPORT ResourceResponseBase(); + WEBCORE_EXPORT ResourceResponseBase(const URL&, const String& mimeType, long long expectedLength, const String& textEncodingName); - void lazyInit(InitLevel) const; + WEBCORE_EXPORT void lazyInit(InitLevel) const; - // The ResourceResponse subclass may "shadow" this method to lazily initialize platform specific fields + // The ResourceResponse subclass should shadow these functions to lazily initialize platform specific fields void platformLazyInit(InitLevel) { } + CertificateInfo platformCertificateInfo() const { return CertificateInfo(); }; + String platformSuggestedFileName() const { return String(); } - // The ResourceResponse subclass may "shadow" this method to compare platform specific fields static bool platformCompare(const ResourceResponse&, const ResourceResponse&) { return true; } +private: + void parseCacheControlDirectives() const; + void updateHeaderParsedState(HTTPHeaderName); + +protected: + bool m_isNull; URL m_url; - String m_mimeType; + AtomicString m_mimeType; long long m_expectedContentLength; - String m_textEncodingName; - String m_suggestedFilename; - String m_httpStatusText; + AtomicString m_textEncodingName; + AtomicString m_httpStatusText; + AtomicString m_httpVersion; HTTPHeaderMap m_httpHeaderFields; - RefPtr<ResourceLoadTiming> m_resourceLoadTiming; + mutable NetworkLoadTiming m_networkLoadTiming; - int m_httpStatusCode; - unsigned m_connectionID; - -private: - mutable double m_cacheControlMaxAge; - mutable double m_age; - mutable double m_date; - mutable double m_expires; - mutable double m_lastModified; + mutable std::optional<CertificateInfo> m_certificateInfo; -public: - bool m_wasCached : 1; - bool m_connectionReused : 1; + int m_httpStatusCode; - bool m_isNull : 1; - private: - const ResourceResponse& asResourceResponse() const; - void parseCacheControlDirectives() const; - void updateHeaderParsedState(const AtomicString& name); - - 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 std::optional<std::chrono::microseconds> m_age; + mutable std::optional<std::chrono::system_clock::time_point> m_date; + mutable std::optional<std::chrono::system_clock::time_point> m_expires; + mutable std::optional<std::chrono::system_clock::time_point> m_lastModified; + mutable ParsedContentRange m_contentRange; + mutable CacheControlDirectives m_cacheControlDirectives; + + mutable bool m_haveParsedCacheControlHeader { false }; + mutable bool m_haveParsedAgeHeader { false }; + mutable bool m_haveParsedDateHeader { false }; + mutable bool m_haveParsedExpiresHeader { false }; + mutable bool m_haveParsedLastModifiedHeader { false }; + mutable bool m_haveParsedContentRangeHeader { false }; + + Source m_source { Source::Unknown }; + + std::optional<SHA1::Digest> m_cacheBodyKey; + + Type m_type { Type::Default }; + bool m_isRedirected { false }; }; 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 { - WTF_MAKE_NONCOPYABLE(CrossThreadResourceResponseDataBase); WTF_MAKE_FAST_ALLOCATED; -public: - CrossThreadResourceResponseDataBase() { } - URL 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; - RefPtr<ResourceLoadTiming> m_resourceLoadTiming; -}; +template<class Encoder> +void ResourceResponseBase::encode(Encoder& encoder) const +{ + encoder << m_isNull; + if (m_isNull) + return; + lazyInit(AllFields); + + encoder << m_url; + encoder << m_mimeType; + encoder << static_cast<int64_t>(m_expectedContentLength); + encoder << m_textEncodingName; + encoder << m_httpStatusText; + encoder << m_httpVersion; + encoder << m_httpHeaderFields; + + // We don't want to put the networkLoadTiming info + // into the disk cache, because we will never use the old info. + if (Encoder::isIPCEncoder) + encoder << m_networkLoadTiming; + + encoder << m_httpStatusCode; + encoder << m_certificateInfo; + encoder.encodeEnum(m_source); + encoder << m_cacheBodyKey; + encoder.encodeEnum(m_type); + encoder << m_isRedirected; +} + +template<class Decoder> +bool ResourceResponseBase::decode(Decoder& decoder, ResourceResponseBase& response) +{ + ASSERT(response.m_isNull); + bool responseIsNull; + if (!decoder.decode(responseIsNull)) + return false; + if (responseIsNull) + return true; + + if (!decoder.decode(response.m_url)) + return false; + if (!decoder.decode(response.m_mimeType)) + return false; + int64_t expectedContentLength; + if (!decoder.decode(expectedContentLength)) + return false; + response.m_expectedContentLength = expectedContentLength; + if (!decoder.decode(response.m_textEncodingName)) + return false; + if (!decoder.decode(response.m_httpStatusText)) + return false; + if (!decoder.decode(response.m_httpVersion)) + return false; + if (!decoder.decode(response.m_httpHeaderFields)) + return false; + // The networkLoadTiming info is only send over IPC and not stored in disk cache. + if (Decoder::isIPCDecoder && !decoder.decode(response.m_networkLoadTiming)) + return false; + if (!decoder.decode(response.m_httpStatusCode)) + return false; + if (!decoder.decode(response.m_certificateInfo)) + return false; + if (!decoder.decodeEnum(response.m_source)) + return false; + if (!decoder.decode(response.m_cacheBodyKey)) + return false; + if (!decoder.decodeEnum(response.m_type)) + return false; + if (!decoder.decode(response.m_isRedirected)) + return false; + response.m_isNull = false; + + return true; +} } // namespace WebCore - -#endif // ResourceResponseBase_h diff --git a/Source/WebCore/platform/network/SocketStreamErrorBase.cpp b/Source/WebCore/platform/network/SocketStreamError.h index bbb5d553f..76995c2a5 100644 --- a/Source/WebCore/platform/network/SocketStreamErrorBase.cpp +++ b/Source/WebCore/platform/network/SocketStreamError.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2009, 2016 Apple Inc. All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,36 +29,42 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "SocketStreamError.h" +#pragma once -namespace WebCore { +#include <wtf/text/WTFString.h> -SocketStreamError SocketStreamErrorBase::copy() const -{ - SocketStreamError errorCopy; - errorCopy.m_errorCode = m_errorCode; - return errorCopy; -} +namespace WebCore { -bool SocketStreamErrorBase::compare(const SocketStreamError& a, const SocketStreamError& b) -{ - if (a.isNull() && b.isNull()) - return true; +class SocketStreamError { +public: + SocketStreamError() + { + } - if (a.isNull() || b.isNull()) - return false; + explicit SocketStreamError(int errorCode) + : m_errorCode(errorCode) + , m_isNull(false) + { + } - if (a.errorCode() != b.errorCode()) - return false; + SocketStreamError(int errorCode, const String& failingURL, const String& localizedDescription) + : m_errorCode(errorCode) + , m_failingURL(failingURL) + , m_localizedDescription(localizedDescription) + , m_isNull(false) + { + } - if (a.failingURL() != b.failingURL()) - return false; + 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; } - if (a.localizedDescription() != b.localizedDescription()) - return false; +private: + int m_errorCode { 0 }; + String m_failingURL; + String m_localizedDescription; + bool m_isNull { true }; +}; - return true; } - -} // namespace WebCore diff --git a/Source/WebCore/platform/network/SocketStreamHandleBase.cpp b/Source/WebCore/platform/network/SocketStreamHandle.cpp index e7c1139dd..a4c6cb24e 100644 --- a/Source/WebCore/platform/network/SocketStreamHandleBase.cpp +++ b/Source/WebCore/platform/network/SocketStreamHandle.cpp @@ -29,28 +29,27 @@ */ #include "config.h" -#include "SocketStreamHandleBase.h" - #include "SocketStreamHandle.h" + #include "SocketStreamHandleClient.h" namespace WebCore { -const unsigned int bufferSize = 100 * 1024 * 1024; +const unsigned bufferSize = 100 * 1024 * 1024; -SocketStreamHandleBase::SocketStreamHandleBase(const URL& url, SocketStreamHandleClient* client) +SocketStreamHandle::SocketStreamHandle(const URL& url, SocketStreamHandleClient& client) : m_url(url) , m_client(client) , m_state(Connecting) { } -SocketStreamHandleBase::SocketStreamState SocketStreamHandleBase::state() const +SocketStreamHandle::SocketStreamState SocketStreamHandle::state() const { return m_state; } -bool SocketStreamHandleBase::send(const char* data, int length) +bool SocketStreamHandle::send(const char* data, size_t length) { if (m_state == Connecting || m_state == Closing) return false; @@ -60,28 +59,28 @@ bool SocketStreamHandleBase::send(const char* data, int length) return false; } m_buffer.append(data, length); - if (m_client) - m_client->didUpdateBufferedAmount(static_cast<SocketStreamHandle*>(this), bufferedAmount()); + m_client.didUpdateBufferedAmount(static_cast<SocketStreamHandle&>(*this), bufferedAmount()); return true; } - int bytesWritten = 0; - if (m_state == Open) - bytesWritten = platformSend(data, length); - if (bytesWritten < 0) - return false; + size_t bytesWritten = 0; + if (m_state == Open) { + if (auto result = platformSend(data, length)) + bytesWritten = result.value(); + else + 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); - if (m_client) - m_client->didUpdateBufferedAmount(static_cast<SocketStreamHandle*>(this), bufferedAmount()); + m_client.didUpdateBufferedAmount(static_cast<SocketStreamHandle&>(*this), bufferedAmount()); } return true; } -void SocketStreamHandleBase::close() +void SocketStreamHandle::close() { if (m_state == Closed) return; @@ -91,21 +90,15 @@ void SocketStreamHandleBase::close() disconnect(); } -void SocketStreamHandleBase::disconnect() +void SocketStreamHandle::disconnect() { - Ref<SocketStreamHandle> protect(*static_cast<SocketStreamHandle*>(this)); // platformClose calls the client, which may make the handle get deallocated immediately. + auto protect = makeRef(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() +bool SocketStreamHandle::sendPendingData() { if (m_state != Open && m_state != Closing) return false; @@ -119,16 +112,18 @@ bool SocketStreamHandleBase::sendPendingData() } bool pending; do { - int bytesWritten = platformSend(m_buffer.firstBlockData(), m_buffer.firstBlockSize()); - pending = bytesWritten != static_cast<int>(m_buffer.firstBlockSize()); - if (bytesWritten <= 0) + auto result = platformSend(m_buffer.firstBlockData(), m_buffer.firstBlockSize()); + if (!result) + return false; + size_t bytesWritten = result.value(); + if (!bytesWritten) return false; + pending = bytesWritten != m_buffer.firstBlockSize(); ASSERT(m_buffer.size() - bytesWritten <= bufferSize); m_buffer.consume(bytesWritten); } while (!pending && !m_buffer.isEmpty()); - if (m_client) - m_client->didUpdateBufferedAmount(static_cast<SocketStreamHandle*>(this), bufferedAmount()); + m_client.didUpdateBufferedAmount(static_cast<SocketStreamHandle&>(*this), bufferedAmount()); return true; } -} // namespace WebCore +} // namespace WebCore diff --git a/Source/WebCore/platform/network/BlobStorageData.h b/Source/WebCore/platform/network/SocketStreamHandle.h index 6535e629a..fbad64195 100644 --- a/Source/WebCore/platform/network/BlobStorageData.h +++ b/Source/WebCore/platform/network/SocketStreamHandle.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2012 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 @@ -28,38 +29,38 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BlobStorageData_h -#define BlobStorageData_h +#pragma once -#include "BlobData.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> +#include "URL.h" +#include <wtf/StreamBuffer.h> +#include <wtf/ThreadSafeRefCounted.h> namespace WebCore { -class BlobStorageData : public RefCounted<BlobStorageData> { +class SocketStreamHandleClient; + +class SocketStreamHandle : public ThreadSafeRefCounted<SocketStreamHandle> { public: - static PassRefPtr<BlobStorageData> create(const String& contentType, const String& contentDisposition) - { - return adoptRef(new BlobStorageData(contentType, contentDisposition)); - } + enum SocketStreamState { Connecting, Open, Closing, Closed }; + virtual ~SocketStreamHandle() { } + SocketStreamState state() const; - const String& contentType() const { return m_data.contentType(); } - const String& contentDisposition() const { return m_data.contentDisposition(); } - const BlobDataItemList& items() const { return m_data.items(); } + bool send(const char* data, size_t length); + void close(); // Disconnect after all data in buffer are sent. + void disconnect(); + size_t bufferedAmount() const { return m_buffer.size(); } -private: - friend class BlobRegistryImpl; +protected: + SocketStreamHandle(const URL&, SocketStreamHandleClient&); - BlobStorageData(const String& contentType, const String& contentDisposition) - { - m_data.setContentType(contentType); - m_data.setContentDisposition(contentDisposition); - } + bool sendPendingData(); + virtual std::optional<size_t> platformSend(const char* data, size_t length) = 0; + virtual void platformClose() = 0; - BlobData m_data; + URL m_url; + SocketStreamHandleClient& m_client; + StreamBuffer<char, 1024 * 1024> m_buffer; + SocketStreamState m_state; }; } // namespace WebCore - -#endif // BlobStorageData_h diff --git a/Source/WebCore/platform/network/SocketStreamHandleBase.h b/Source/WebCore/platform/network/SocketStreamHandleBase.h deleted file mode 100644 index f79472021..000000000 --- a/Source/WebCore/platform/network/SocketStreamHandleBase.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2009, 2012 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 "URL.h" - -#include <wtf/StreamBuffer.h> - -namespace WebCore { - - class SocketStreamHandle; - class SocketStreamHandleClient; - - class SocketStreamHandleBase { - public: - enum SocketStreamState { Connecting, Open, Closing, Closed }; - virtual ~SocketStreamHandleBase() { } - SocketStreamState state() const; - - bool send(const char* data, int length); - void close(); // Disconnect after all data in buffer are sent. - void disconnect(); - size_t bufferedAmount() const { return m_buffer.size(); } - - SocketStreamHandleClient* client() const { return m_client; } - void setClient(SocketStreamHandleClient*); - - protected: - SocketStreamHandleBase(const URL&, SocketStreamHandleClient*); - - bool sendPendingData(); - virtual int platformSend(const char* data, int length) = 0; - virtual void platformClose() = 0; - - URL m_url; - SocketStreamHandleClient* m_client; - StreamBuffer<char, 1024 * 1024> 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 index 5a8c4d50e..e26b9a623 100644 --- a/Source/WebCore/platform/network/SocketStreamHandleClient.h +++ b/Source/WebCore/platform/network/SocketStreamHandleClient.h @@ -29,33 +29,24 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SocketStreamHandleClient_h -#define SocketStreamHandleClient_h +#pragma once -namespace WebCore { - - class AuthenticationChallenge; - class URL; - class SocketStreamError; - class SocketStreamHandle; +#include <wtf/Optional.h> - class SocketStreamHandleClient { - public: - virtual ~SocketStreamHandleClient() { } - - virtual void willOpenSocketStream(SocketStreamHandle*) { } - virtual void didOpenSocketStream(SocketStreamHandle*) { } - virtual void didCloseSocketStream(SocketStreamHandle*) { } - virtual void didReceiveSocketStreamData(SocketStreamHandle*, const char* /*data*/, int /*length*/) { } - virtual void didUpdateBufferedAmount(SocketStreamHandle*, size_t /*bufferedAmount*/) { } +namespace WebCore { - virtual void didFailSocketStream(SocketStreamHandle*, const SocketStreamError&) { } +class SocketStreamError; +class SocketStreamHandle; - // No authentication for streams per se, but proxy may ask for credentials. - virtual void didReceiveAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&) { } - virtual void didCancelAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&) { } - }; +class SocketStreamHandleClient { +public: + virtual ~SocketStreamHandleClient() { } -} // namespace WebCore + virtual void didOpenSocketStream(SocketStreamHandle&) = 0; + virtual void didCloseSocketStream(SocketStreamHandle&) = 0; + virtual void didReceiveSocketStreamData(SocketStreamHandle&, const char* data, std::optional<size_t> length) = 0; + virtual void didUpdateBufferedAmount(SocketStreamHandle&, size_t bufferedAmount) = 0; + virtual void didFailSocketStream(SocketStreamHandle&, const SocketStreamError&) = 0; +}; -#endif // SocketStreamHandleClient_h +} // namespace WebCore diff --git a/Source/WebCore/platform/network/SynchronousLoaderClient.cpp b/Source/WebCore/platform/network/SynchronousLoaderClient.cpp index 994682fb1..87c994b2f 100644 --- a/Source/WebCore/platform/network/SynchronousLoaderClient.cpp +++ b/Source/WebCore/platform/network/SynchronousLoaderClient.cpp @@ -36,16 +36,16 @@ SynchronousLoaderClient::~SynchronousLoaderClient() { } -void SynchronousLoaderClient::willSendRequest(ResourceHandle* handle, ResourceRequest& request, const ResourceResponse& /*redirectResponse*/) +ResourceRequest SynchronousLoaderClient::willSendRequest(ResourceHandle* handle, ResourceRequest&& request, ResourceResponse&&) { // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. if (protocolHostAndPortAreEqual(handle->firstRequest().url(), request.url())) - return; + return WTFMove(request); ASSERT(m_error.isNull()); m_error = platformBadResponseError(); m_isDone = true; - request = ResourceRequest(); + return { }; } bool SynchronousLoaderClient::shouldUseCredentialStorage(ResourceHandle*) @@ -62,9 +62,9 @@ bool SynchronousLoaderClient::canAuthenticateAgainstProtectionSpace(ResourceHand } #endif -void SynchronousLoaderClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) +void SynchronousLoaderClient::didReceiveResponse(ResourceHandle*, ResourceResponse&& response) { - m_response = response; + m_response = WTFMove(response); } void SynchronousLoaderClient::didReceiveData(ResourceHandle*, const char* data, unsigned length, int /*encodedDataLength*/) diff --git a/Source/WebCore/platform/network/SynchronousLoaderClient.h b/Source/WebCore/platform/network/SynchronousLoaderClient.h index 4619f8f35..9ca0e6a5d 100644 --- a/Source/WebCore/platform/network/SynchronousLoaderClient.h +++ b/Source/WebCore/platform/network/SynchronousLoaderClient.h @@ -32,13 +32,8 @@ namespace WebCore { -class SynchronousLoaderClient : public ResourceHandleClient { +class SynchronousLoaderClient final : public ResourceHandleClient { public: - static PassOwnPtr<SynchronousLoaderClient> create() - { - return adoptPtr(new SynchronousLoaderClient); - } - virtual ~SynchronousLoaderClient(); void setAllowStoredCredentials(bool allow) { m_allowStoredCredentials = allow; } @@ -47,31 +42,25 @@ public: const ResourceError& error() const { return m_error; } bool isDone() { return m_isDone; } - static ResourceError platformBadResponseError(); + WEBCORE_EXPORT static ResourceError platformBadResponseError(); private: - SynchronousLoaderClient() - : m_allowStoredCredentials(false) - , m_isDone(false) - { - } - - virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& /*redirectResponse*/) override; - virtual bool shouldUseCredentialStorage(ResourceHandle*) override; - virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge&) override; - virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&) override; - virtual void didReceiveData(ResourceHandle*, const char*, unsigned, int /*encodedDataLength*/) override; - virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/) override; - virtual void didFail(ResourceHandle*, const ResourceError&) override; + ResourceRequest willSendRequest(ResourceHandle*, ResourceRequest&&, ResourceResponse&&) override; + bool shouldUseCredentialStorage(ResourceHandle*) override; + void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge&) override; + void didReceiveResponse(ResourceHandle*, ResourceResponse&&) override; + void didReceiveData(ResourceHandle*, const char*, unsigned, int /*encodedDataLength*/) override; + void didFinishLoading(ResourceHandle*, double /*finishTime*/) override; + void didFail(ResourceHandle*, const ResourceError&) override; #if USE(PROTECTION_SPACE_AUTH_CALLBACK) - virtual bool canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&) override; + bool canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace&) override; #endif - bool m_allowStoredCredentials; + bool m_allowStoredCredentials { false }; ResourceResponse m_response; Vector<char> m_data; ResourceError m_error; - bool m_isDone; + bool m_isDone { false }; }; } diff --git a/Source/WebCore/platform/network/create-http-header-name-table b/Source/WebCore/platform/network/create-http-header-name-table new file mode 100755 index 000000000..755d22e94 --- /dev/null +++ b/Source/WebCore/platform/network/create-http-header-name-table @@ -0,0 +1,245 @@ +#!/usr/bin/env python +# Copyright (c) 2014 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. +# * 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. + +import os +import sys + +program_name = os.path.basename(__file__) +if len(sys.argv) < 2: + sys.stderr.write("Usage: %s INPUT_FILE GPERF_EXECUTABLE\n" % program_name) + exit(1) + +input_path = sys.argv[1] +input_file = open(input_path) + +http_header_name_to_id = { } +http_header_names = [] + +for line in input_file.xreadlines(): + http_header_name = line.strip() + if not http_header_name or http_header_name[:2] == '//': + continue + + http_header_name_to_id[http_header_name] = http_header_name.replace('-', '').replace('.', '') + + http_header_names.append(http_header_name) + +input_file.close() + +http_header_names.sort() + +gperf_file = open('HTTPHeaderNames.gperf', 'w') +gperf_file.write(''' +%{ +/* + * Copyright (C) 2014 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. + */ + +/// This file is generated by create-http-header-name-table, do not edit. + +#include "config.h" +#include "HTTPHeaderNames.h" + +#include <wtf/text/StringView.h> + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wdeprecated-register" +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#endif + +namespace WebCore { + +static const struct HeaderNameString { + const char* const name; + unsigned length; +} headerNameStrings[] = { +''') + +for http_header_name in http_header_names: + gperf_file.write(' { "%s", %u },\n' % (http_header_name, len(http_header_name))) + +gperf_file.write('};\n\n') + +gperf_file.write(''' +%} + +%language=C++ +%readonly-tables +%global-table +%compare-strncmp +%ignore-case +%struct-type +struct HeaderNameHashEntry { + const char* name; + HTTPHeaderName headerName; +}; +%define class-name HTTPHeaderNamesHash +%define lookup-function-name findHeaderNameImpl +%define hash-function-name header_name_hash_function +%define word-array-name header_name_wordlist +%enum +%% +''') + +for http_header_name in http_header_names: + gperf_file.write('%s, HTTPHeaderName::%s\n' % (http_header_name, http_header_name_to_id[http_header_name])) + +gperf_file.write('''%% +bool findHTTPHeaderName(StringView stringView, HTTPHeaderName& headerName) +{ + unsigned length = stringView.length(); + if (length > maxHTTPHeaderNameLength || length < minHTTPHeaderNameLength) + return false; + + if (stringView.is8Bit()) { + if (auto nameAndString = HTTPHeaderNamesHash::findHeaderNameImpl(reinterpret_cast<const char*>(stringView.characters8()), length)) { + headerName = nameAndString->headerName; + return true; + } + } else { + LChar characters[maxHTTPHeaderNameLength]; + for (unsigned i = 0; i < length; ++i) { + UChar character = stringView.characters16()[i]; + if (!isASCII(character)) + return false; + + characters[i] = static_cast<LChar>(character); + } + + if (auto nameAndString = HTTPHeaderNamesHash::findHeaderNameImpl(reinterpret_cast<const char*>(characters), length)) { + headerName = nameAndString->headerName; + return true; + } + } + + return false; +} + +StringView httpHeaderNameString(HTTPHeaderName headerName) +{ + ASSERT(static_cast<unsigned>(headerName) < numHTTPHeaderNames); + + const auto& name = headerNameStrings[static_cast<unsigned>(headerName)]; + + return StringView { reinterpret_cast<const LChar*>(name.name), static_cast<unsigned>(name.length) }; +} + +} // namespace WebCore + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +''') + +gperf_file.close() + +header_file = open('HTTPHeaderNames.h', 'w') +header_file.write(''' +/* + * Copyright (C) 2014 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. + */ + +/// This file is generated by create-http-header-name-table, do not edit. + +#ifndef HTTPHeaderNames_h +#define HTTPHeaderNames_h + +#include <wtf/Forward.h> + +namespace WebCore { + +enum class HTTPHeaderName { +''') + +for http_header_name in http_header_names: + header_file.write(' %s,\n' % http_header_name_to_id[http_header_name]) + +header_file.write('};\n\n') +header_file.write('const unsigned numHTTPHeaderNames = %u;\n' % len(http_header_names)); +header_file.write('const size_t minHTTPHeaderNameLength = %u;\n' % len(min(http_header_names, key=len))); +header_file.write('const size_t maxHTTPHeaderNameLength = %u;\n' % len(max(http_header_names, key=len))); +header_file.write(''' +bool findHTTPHeaderName(StringView, HTTPHeaderName&); +WEBCORE_EXPORT StringView httpHeaderNameString(HTTPHeaderName); + +} // namespace WebCore + +#endif // HTTPHeaderNames_h +''') +header_file.close() + +gperf = os.getenv('GPERF') or sys.argv[2] + +if os.system('%s --key-positions="*" -D -n -s 2 HTTPHeaderNames.gperf --output-file=HTTPHeaderNames.cpp' % gperf): + sys.stderr.write('Failed to run gperf.') + exit(1) diff --git a/Source/WebCore/platform/network/gtk/CredentialBackingStore.cpp b/Source/WebCore/platform/network/gtk/CredentialBackingStore.cpp deleted file mode 100644 index 086dbc15d..000000000 --- a/Source/WebCore/platform/network/gtk/CredentialBackingStore.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2012, 2013 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 IGALIA S.L. ``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 "CredentialBackingStore.h" - -#if ENABLE(CREDENTIAL_STORAGE) -#define SECRET_WITH_UNSTABLE 1 -#define SECRET_API_SUBJECT_TO_CHANGE 1 -#include "AuthenticationChallenge.h" -#include "GRefPtrGtk.h" -#include <glib/gi18n-lib.h> -#include <libsecret/secret.h> -#include <libsoup/soup.h> -#include <wtf/gobject/GUniquePtr.h> -#include <wtf/text/CString.h> -#endif - -namespace WebCore { - -CredentialBackingStore& credentialBackingStore() -{ - DEFINE_STATIC_LOCAL(CredentialBackingStore, backingStore, ()); - return backingStore; -} - -#if ENABLE(CREDENTIAL_STORAGE) -static GRefPtr<GHashTable> createAttributeHashTableFromChallenge(const AuthenticationChallenge& challenge, const Credential& credential = Credential()) -{ - SoupURI* uri = soup_message_get_uri(challenge.soupMessage()); - GRefPtr<GHashTable> attributes = adoptGRef(secret_attributes_build( - SECRET_SCHEMA_COMPAT_NETWORK, - "domain", soup_auth_get_realm(challenge.soupAuth()), - "server", uri->host, - "protocol", uri->scheme, - "authtype", soup_auth_get_scheme_name(challenge.soupAuth()), - "port", uri->port, - NULL)); - if (credential.isEmpty()) - return attributes; - - g_hash_table_insert(attributes.get(), g_strdup("user"), g_strdup(credential.user().utf8().data())); - return attributes; -} - -struct CredentialForChallengeAsyncReadyCallbackData { - CredentialBackingStore::CredentialForChallengeCallback callback; - void* data; -}; - -static void credentialForChallengeAsyncReadyCallback(SecretService* service, GAsyncResult* asyncResult, CredentialForChallengeAsyncReadyCallbackData* callbackData) -{ - CredentialBackingStore::CredentialForChallengeCallback callback = callbackData->callback; - void* data = callbackData->data; - delete callbackData; - - GUniqueOutPtr<GError> error; - GUniquePtr<GList> elements(secret_service_search_finish(service, asyncResult, &error.outPtr())); - if (error || !elements || !elements->data) { - callback(Credential(), data); - return; - } - - GRefPtr<SecretItem> secretItem = adoptGRef(static_cast<SecretItem*>(elements->data)); - GRefPtr<GHashTable> attributes = adoptGRef(secret_item_get_attributes(secretItem.get())); - String user = String::fromUTF8(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "user"))); - if (user.isEmpty()) { - callback(Credential(), data); - return; - } - - size_t length; - GRefPtr<SecretValue> secretValue = adoptGRef(secret_item_get_secret(secretItem.get())); - const char* passwordData = secret_value_get(secretValue.get(), &length); - String password = String::fromUTF8(passwordData, length); - - callback(Credential(user, password, CredentialPersistencePermanent), data); -} -#endif // ENABLE(CREDENTIAL_STORAGE) - -void CredentialBackingStore::credentialForChallenge(const AuthenticationChallenge& challenge, CredentialForChallengeCallback callback, void* data) -{ -#if ENABLE(CREDENTIAL_STORAGE) - // The default flag only returns the most recent item, not all of them. - SecretSearchFlags searchFlags = static_cast<SecretSearchFlags>(SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS); - CredentialForChallengeAsyncReadyCallbackData* callbackData = new CredentialForChallengeAsyncReadyCallbackData; - callbackData->callback = callback; - callbackData->data = data; - - secret_service_search( - 0, // The default SecretService. - SECRET_SCHEMA_COMPAT_NETWORK, - createAttributeHashTableFromChallenge(challenge).get(), - searchFlags, - 0, // cancellable - reinterpret_cast<GAsyncReadyCallback>(credentialForChallengeAsyncReadyCallback), - callbackData); -#else - callback(Credential(), data); -#endif // ENABLE(CREDENTIAL_STORAGE) -} - -void CredentialBackingStore::storeCredentialsForChallenge(const AuthenticationChallenge& challenge, const Credential& credential) -{ -#if ENABLE(CREDENTIAL_STORAGE) - CString utf8Password = credential.password().utf8(); - GRefPtr<SecretValue> newSecretValue = adoptGRef(secret_value_new(utf8Password.data(), utf8Password.length(), "text/plain")); - - secret_service_store( - 0, // The default SecretService. - SECRET_SCHEMA_COMPAT_NETWORK, - createAttributeHashTableFromChallenge(challenge, credential).get(), - SECRET_COLLECTION_DEFAULT, - _("WebKitGTK+ password"), - newSecretValue.get(), - 0, // cancellable - 0, // callback - 0); // data -#endif // ENABLE(CREDENTIAL_STORAGE) -} - -} // namespace WebCore diff --git a/Source/WebCore/platform/network/gtk/CredentialBackingStore.h b/Source/WebCore/platform/network/gtk/CredentialBackingStore.h deleted file mode 100644 index 20da9a157..000000000 --- a/Source/WebCore/platform/network/gtk/CredentialBackingStore.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2012 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 IGALIA S.L. ``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 CredentialBackingStore_h -#define CredentialBackingStore_h - -#include "Credential.h" - -namespace WebCore { - -class AuthenticationChallenge; - -class CredentialBackingStore { -WTF_MAKE_NONCOPYABLE(CredentialBackingStore); -public: - friend CredentialBackingStore& credentialBackingStore(); - virtual ~CredentialBackingStore() { } - - typedef void (*CredentialForChallengeCallback)(const Credential&, void* data); - - void credentialForChallenge(const AuthenticationChallenge&, CredentialForChallengeCallback, void* data); - void storeCredentialsForChallenge(const AuthenticationChallenge&, const Credential&); -private: - CredentialBackingStore() { } -}; - -CredentialBackingStore& credentialBackingStore(); - -} // namespace WebCore - -#endif // CredentialBackingStore_h diff --git a/Source/WebCore/platform/network/soup/AuthenticationChallenge.h b/Source/WebCore/platform/network/soup/AuthenticationChallenge.h index 48cf591be..c6d4bd1dd 100644 --- a/Source/WebCore/platform/network/soup/AuthenticationChallenge.h +++ b/Source/WebCore/platform/network/soup/AuthenticationChallenge.h @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -44,10 +44,8 @@ public: { } - AuthenticationChallenge(SoupSession*, SoupMessage*, SoupAuth*, bool retrying, AuthenticationClient*); + AuthenticationChallenge(SoupMessage*, SoupAuth*, bool retrying, AuthenticationClient* = nullptr); AuthenticationClient* authenticationClient() const { return m_authenticationClient.get(); } - SoupSession* soupSession() const { return m_soupSession.get(); } - SoupMessage* soupMessage() const { return m_soupMessage.get(); } SoupAuth* soupAuth() const { return m_soupAuth.get(); } void setProposedCredential(const Credential& credential) { m_proposedCredential = credential; } @@ -55,8 +53,6 @@ private: friend class AuthenticationChallengeBase; static bool platformCompare(const AuthenticationChallenge&, const AuthenticationChallenge&); - GRefPtr<SoupSession> m_soupSession; - GRefPtr<SoupMessage> m_soupMessage; GRefPtr<SoupAuth> m_soupAuth; RefPtr<AuthenticationClient> m_authenticationClient; }; diff --git a/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp b/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp index 2e7d02852..97e923c8a 100644 --- a/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp +++ b/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY IGALIA S.L. ``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 + * 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 @@ -24,6 +24,9 @@ */ #include "config.h" + +#if USE(SOUP) + #include "AuthenticationChallenge.h" #include "ResourceError.h" @@ -63,14 +66,12 @@ static ProtectionSpace protectionSpaceFromSoupAuthAndMessage(SoupAuth* soupAuth, String::fromUTF8(soup_auth_get_realm(soupAuth)), scheme); } -AuthenticationChallenge::AuthenticationChallenge(SoupSession* soupSession, SoupMessage* soupMessage, SoupAuth* soupAuth, bool retrying, AuthenticationClient* client) +AuthenticationChallenge::AuthenticationChallenge(SoupMessage* soupMessage, SoupAuth* soupAuth, bool retrying, AuthenticationClient* client) : AuthenticationChallengeBase(protectionSpaceFromSoupAuthAndMessage(soupAuth, soupMessage), Credential(), // proposedCredentials retrying ? 1 : 0, // previousFailureCount soupMessage, // failureResponse ResourceError::authenticationError(soupMessage)) - , m_soupSession(soupSession) - , m_soupMessage(soupMessage) , m_soupAuth(soupAuth) , m_authenticationClient(client) { @@ -78,9 +79,9 @@ AuthenticationChallenge::AuthenticationChallenge(SoupSession* soupSession, SoupM bool AuthenticationChallenge::platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b) { - return a.soupSession() == b.soupSession() - && a.soupMessage() == b.soupMessage() - && a.soupAuth() == b.soupAuth(); + return a.soupAuth() == b.soupAuth(); } } // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/CertificateInfo.cpp b/Source/WebCore/platform/network/soup/CertificateInfo.cpp index d5fae4dbf..1db77a323 100644 --- a/Source/WebCore/platform/network/soup/CertificateInfo.cpp +++ b/Source/WebCore/platform/network/soup/CertificateInfo.cpp @@ -24,6 +24,9 @@ */ #include "config.h" + +#if USE(SOUP) + #include "CertificateInfo.h" #include <ResourceError.h> @@ -60,3 +63,5 @@ CertificateInfo::~CertificateInfo() } } // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/CertificateInfo.h b/Source/WebCore/platform/network/soup/CertificateInfo.h index 94e4ff585..206cdef68 100644 --- a/Source/WebCore/platform/network/soup/CertificateInfo.h +++ b/Source/WebCore/platform/network/soup/CertificateInfo.h @@ -27,8 +27,9 @@ #ifndef CertificateInfo_h #define CertificateInfo_h +#include "NotImplemented.h" #include <libsoup/soup.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> namespace WebCore { @@ -48,6 +49,8 @@ public: GTlsCertificateFlags tlsErrors() const { return m_tlsErrors; } void setTLSErrors(GTlsCertificateFlags tlsErrors) { m_tlsErrors = tlsErrors; } + bool containsNonRootSHA1SignedCertificate() const { notImplemented(); return false; } + private: GRefPtr<GTlsCertificate> m_certificate; GTlsCertificateFlags m_tlsErrors; diff --git a/Source/WebCore/platform/network/soup/CookieJarSoup.cpp b/Source/WebCore/platform/network/soup/CookieJarSoup.cpp index fec9dd544..31f009d17 100644 --- a/Source/WebCore/platform/network/soup/CookieJarSoup.cpp +++ b/Source/WebCore/platform/network/soup/CookieJarSoup.cpp @@ -19,55 +19,22 @@ */ #include "config.h" -#include "CookieJarSoup.h" + +#if USE(SOUP) #include "Cookie.h" #include "GUniquePtrSoup.h" -#include "URL.h" +#include "NetworkStorageSession.h" #include "NetworkingContext.h" #include "PlatformCookieJar.h" #include "SoupNetworkSession.h" -#include <wtf/gobject/GRefPtr.h> +#include "URL.h" +#include <wtf/DateMath.h> +#include <wtf/glib/GRefPtr.h> #include <wtf/text/CString.h> namespace WebCore { -static SoupCookieJar* cookieJarForSession(const NetworkStorageSession& session) -{ - return session.soupNetworkSession().cookieJar(); -} - -static GRefPtr<SoupCookieJar>& defaultCookieJar() -{ - DEFINE_STATIC_LOCAL(GRefPtr<SoupCookieJar>, cookieJar, ()); - return cookieJar; -} - -SoupCookieJar* soupCookieJar() -{ - if (GRefPtr<SoupCookieJar>& jar = defaultCookieJar()) - return jar.get(); - - SoupCookieJar* jar = soup_cookie_jar_new(); - soup_cookie_jar_set_accept_policy(jar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); - setSoupCookieJar(jar); - return jar; -} - -SoupCookieJar* createPrivateBrowsingCookieJar() -{ - SoupCookieJar* jar = soup_cookie_jar_new(); - - soup_cookie_jar_set_accept_policy(jar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); - - return jar; -} - -void setSoupCookieJar(SoupCookieJar* jar) -{ - defaultCookieJar() = jar; -} - static inline bool httpOnlyCookieExists(const GSList* cookies, const gchar* name, const gchar* path) { for (const GSList* iter = cookies; iter; iter = g_slist_next(iter)) { @@ -84,9 +51,7 @@ static inline bool httpOnlyCookieExists(const GSList* cookies, const gchar* name void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, const String& value) { - SoupCookieJar* jar = cookieJarForSession(session); - if (!jar) - return; + SoupCookieJar* jar = session.cookieStorage(); GUniquePtr<SoupURI> origin = url.createSoupURI(); GUniquePtr<SoupURI> firstPartyURI = firstParty.createSoupURI(); @@ -118,12 +83,8 @@ void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstPar static String cookiesForSession(const NetworkStorageSession& session, const URL& url, bool forHTTPHeader) { - SoupCookieJar* jar = cookieJarForSession(session); - if (!jar) - return String(); - GUniquePtr<SoupURI> uri = url.createSoupURI(); - GUniquePtr<char> cookies(soup_cookie_jar_get_cookies(jar, uri.get(), forHTTPHeader)); + GUniquePtr<char> cookies(soup_cookie_jar_get_cookies(session.cookieStorage(), uri.get(), forHTTPHeader)); return String::fromUTF8(cookies.get()); } @@ -139,18 +100,15 @@ String cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const bool cookiesEnabled(const NetworkStorageSession& session, const URL& /*firstParty*/, const URL& /*url*/) { - return !!cookieJarForSession(session); + auto policy = soup_cookie_jar_get_accept_policy(session.cookieStorage()); + return policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS || policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY; } bool getRawCookies(const NetworkStorageSession& session, const URL& /*firstParty*/, const URL& url, Vector<Cookie>& rawCookies) { rawCookies.clear(); - SoupCookieJar* jar = cookieJarForSession(session); - if (!jar) - return false; - GUniquePtr<SoupURI> uri = url.createSoupURI(); - GUniquePtr<GSList> cookies(soup_cookie_jar_get_cookie_list(jar, uri.get(), TRUE)); + GUniquePtr<GSList> cookies(soup_cookie_jar_get_cookie_list(session.cookieStorage(), uri.get(), TRUE)); if (!cookies) return false; @@ -167,9 +125,7 @@ bool getRawCookies(const NetworkStorageSession& session, const URL& /*firstParty void deleteCookie(const NetworkStorageSession& session, const URL& url, const String& name) { - SoupCookieJar* jar = cookieJarForSession(session); - if (!jar) - return; + SoupCookieJar* jar = session.cookieStorage(); GUniquePtr<SoupURI> uri = url.createSoupURI(); GUniquePtr<GSList> cookies(soup_cookie_jar_get_cookie_list(jar, uri.get(), TRUE)); @@ -188,10 +144,36 @@ void deleteCookie(const NetworkStorageSession& session, const URL& url, const St } } +static SoupDate* msToSoupDate(double ms) +{ + int year = msToYear(ms); + int dayOfYear = dayInYear(ms, year); + bool leapYear = isLeapYear(year); + return soup_date_new(year, monthFromDayInYear(dayOfYear, leapYear), dayInMonthFromDayInYear(dayOfYear, leapYear), msToHours(ms), msToMinutes(ms), static_cast<int>(ms / 1000) % 60); +} + +static SoupCookie* toSoupCookie(const Cookie& cookie) +{ + SoupCookie* soupCookie = soup_cookie_new(cookie.name.utf8().data(), cookie.value.utf8().data(), + cookie.domain.utf8().data(), cookie.path.utf8().data(), -1); + soup_cookie_set_http_only(soupCookie, cookie.httpOnly); + soup_cookie_set_secure(soupCookie, cookie.secure); + if (!cookie.session) { + SoupDate* date = msToSoupDate(cookie.expires); + soup_cookie_set_expires(soupCookie, date); + soup_date_free(date); + } + return soupCookie; +} + +void addCookie(const NetworkStorageSession& session, const URL&, const Cookie& cookie) +{ + soup_cookie_jar_add_cookie(session.cookieStorage(), toSoupCookie(cookie)); +} + void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<String>& hostnames) { - SoupCookieJar* cookieJar = cookieJarForSession(session); - GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar)); + GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(session.cookieStorage())); for (GSList* item = cookies.get(); item; item = g_slist_next(item)) { SoupCookie* cookie = static_cast<SoupCookie*>(item->data); if (cookie->domain) @@ -200,22 +182,26 @@ void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<Strin } } -void deleteCookiesForHostname(const NetworkStorageSession& session, const String& hostname) +void deleteCookiesForHostnames(const NetworkStorageSession& session, const Vector<String>& hostnames) { - CString hostNameString = hostname.utf8(); - SoupCookieJar* cookieJar = cookieJarForSession(session); - GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar)); - for (GSList* item = cookies.get(); item; item = g_slist_next(item)) { - SoupCookie* cookie = static_cast<SoupCookie*>(item->data); - if (soup_cookie_domain_matches(cookie, hostNameString.data())) - soup_cookie_jar_delete_cookie(cookieJar, cookie); - soup_cookie_free(cookie); + SoupCookieJar* cookieJar = session.cookieStorage(); + + for (const auto& hostname : hostnames) { + CString hostNameString = hostname.utf8(); + + GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar)); + for (GSList* item = cookies.get(); item; item = g_slist_next(item)) { + SoupCookie* cookie = static_cast<SoupCookie*>(item->data); + if (soup_cookie_domain_matches(cookie, hostNameString.data())) + soup_cookie_jar_delete_cookie(cookieJar, cookie); + soup_cookie_free(cookie); + } } } void deleteAllCookies(const NetworkStorageSession& session) { - SoupCookieJar* cookieJar = cookieJarForSession(session); + SoupCookieJar* cookieJar = session.cookieStorage(); GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar)); for (GSList* item = cookies.get(); item; item = g_slist_next(item)) { SoupCookie* cookie = static_cast<SoupCookie*>(item->data); @@ -224,4 +210,15 @@ void deleteAllCookies(const NetworkStorageSession& session) } } +void deleteAllCookiesModifiedSince(const NetworkStorageSession& session, std::chrono::system_clock::time_point timestamp) +{ + // FIXME: Add support for deleting cookies modified since the given timestamp. It should probably be added to libsoup. + if (timestamp == std::chrono::system_clock::from_time_t(0)) + deleteAllCookies(session); + else + g_warning("Deleting cookies modified since a given time span is not supported yet"); +} + } + +#endif diff --git a/Source/WebCore/platform/network/soup/CookieStorageSoup.cpp b/Source/WebCore/platform/network/soup/CookieStorageSoup.cpp index 65817a1bd..ea6a6b292 100644 --- a/Source/WebCore/platform/network/soup/CookieStorageSoup.cpp +++ b/Source/WebCore/platform/network/soup/CookieStorageSoup.cpp @@ -19,34 +19,20 @@ #include "config.h" #include "CookieStorage.h" -#include "CookieJarSoup.h" -#include "NotImplemented.h" - -#include <stdio.h> +#if USE(SOUP) namespace WebCore { -static CookieChangeCallbackPtr cookieChangeCallback; - -static void soupCookiesChanged(SoupCookieJar* jar, SoupCookie*, SoupCookie*, gpointer) +void startObservingCookieChanges(const NetworkStorageSession&, std::function<void ()>&&) { - if (jar != soupCookieJar()) - return; - cookieChangeCallback(); + ASSERT_NOT_REACHED(); } -void startObservingCookieChanges(CookieChangeCallbackPtr callback) +void stopObservingCookieChanges(const NetworkStorageSession&) { - ASSERT(!cookieChangeCallback); - cookieChangeCallback = callback; - - g_signal_connect(soupCookieJar(), "changed", G_CALLBACK(soupCookiesChanged), 0); + ASSERT_NOT_REACHED(); } -void stopObservingCookieChanges() -{ - g_signal_handlers_disconnect_by_func(soupCookieJar(), reinterpret_cast<void*>(soupCookiesChanged), 0); - cookieChangeCallback = 0; } -} +#endif diff --git a/Source/WebCore/platform/network/soup/CredentialStorageSoup.cpp b/Source/WebCore/platform/network/soup/CredentialStorageSoup.cpp index 54ccad056..e5ec16f95 100644 --- a/Source/WebCore/platform/network/soup/CredentialStorageSoup.cpp +++ b/Source/WebCore/platform/network/soup/CredentialStorageSoup.cpp @@ -26,6 +26,8 @@ #include "config.h" #include "CredentialStorage.h" +#if USE(SOUP) + #include "Credential.h" namespace WebCore { @@ -36,3 +38,5 @@ Credential CredentialStorage::getFromPersistentStorage(const ProtectionSpace&) } } // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/DNSSoup.cpp b/Source/WebCore/platform/network/soup/DNSSoup.cpp index 6f630b5b8..ca11457dd 100644 --- a/Source/WebCore/platform/network/soup/DNSSoup.cpp +++ b/Source/WebCore/platform/network/soup/DNSSoup.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. * Copyright (C) 2009, 2012 Igalia S.L. * * Redistribution and use in source and binary forms, with or without @@ -14,7 +14,7 @@ * 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 + * 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 @@ -28,31 +28,74 @@ #include "DNS.h" #include "DNSResolveQueue.h" +#if USE(SOUP) + +#include "NetworkStorageSession.h" #include "SoupNetworkSession.h" #include <libsoup/soup.h> #include <wtf/MainThread.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> namespace WebCore { -// There is no current reliable way to know if we're behind a proxy at -// this level. We'll have to implement it in -// SoupSession/SoupProxyURIResolver/GProxyResolver -bool DNSResolveQueue::platformProxyIsEnabledInSystemPreferences() +// Initially true to ensure prefetch stays disabled until we have proxy settings. +static bool isUsingHttpProxy = true; +static bool isUsingHttpsProxy = true; + +static bool didResolveProxy(char** uris) +{ + // We have a list of possible proxies to use for the URI. If the first item in the list is + // direct:// (the usual case), then the user prefers not to use a proxy. This is similar to + // resolving hostnames: there could be many possibilities returned in order of preference, and + // if we're trying to connect we should attempt each one in order, but here we are not trying + // to connect, merely to decide whether a proxy "should" be used. + return uris && *uris && strcmp(*uris, "direct://"); +} + +static void didResolveProxy(GProxyResolver* resolver, GAsyncResult* result, bool* isUsingProxyType, bool* isUsingProxy) +{ + GUniqueOutPtr<GError> error; + GUniquePtr<char*> uris(g_proxy_resolver_lookup_finish(resolver, result, &error.outPtr())); + if (error) { + WTFLogAlways("Error determining system proxy settings: %s", error->message); + return; + } + + *isUsingProxyType = didResolveProxy(uris.get()); + *isUsingProxy = isUsingHttpProxy || isUsingHttpsProxy; +} + +static void proxyResolvedForHttpUriCallback(GObject* source, GAsyncResult* result, void* userData) { - return false; + didResolveProxy(G_PROXY_RESOLVER(source), result, &isUsingHttpProxy, static_cast<bool*>(userData)); +} + +static void proxyResolvedForHttpsUriCallback(GObject* source, GAsyncResult* result, void* userData) +{ + didResolveProxy(G_PROXY_RESOLVER(source), result, &isUsingHttpsProxy, static_cast<bool*>(userData)); +} + +void DNSResolveQueue::updateIsUsingProxy() +{ + GRefPtr<GProxyResolver> resolver; + g_object_get(NetworkStorageSession::defaultStorageSession().getOrCreateSoupNetworkSession().soupSession(), "proxy-resolver", &resolver.outPtr(), nullptr); + ASSERT(resolver); + + g_proxy_resolver_lookup_async(resolver.get(), "http://example.com/", nullptr, proxyResolvedForHttpUriCallback, &m_isUsingProxy); + g_proxy_resolver_lookup_async(resolver.get(), "https://example.com/", nullptr, proxyResolvedForHttpsUriCallback, &m_isUsingProxy); } static void resolvedCallback(SoupAddress*, guint, void*) { - DNSResolveQueue::shared().decrementRequestCount(); + DNSResolveQueue::singleton().decrementRequestCount(); } void DNSResolveQueue::platformResolve(const String& hostname) { ASSERT(isMainThread()); - soup_session_prefetch_dns(SoupNetworkSession::defaultSession().soupSession(), hostname.utf8().data(), nullptr, resolvedCallback, nullptr); + soup_session_prefetch_dns(NetworkStorageSession::defaultStorageSession().getOrCreateSoupNetworkSession().soupSession(), hostname.utf8().data(), nullptr, resolvedCallback, nullptr); } void prefetchDNS(const String& hostname) @@ -61,7 +104,9 @@ void prefetchDNS(const String& hostname) if (hostname.isEmpty()) return; - DNSResolveQueue::shared().add(hostname); + DNSResolveQueue::singleton().add(hostname); } } + +#endif diff --git a/Source/WebCore/platform/network/soup/GRefPtrSoup.cpp b/Source/WebCore/platform/network/soup/GRefPtrSoup.cpp new file mode 100644 index 000000000..009aec168 --- /dev/null +++ b/Source/WebCore/platform/network/soup/GRefPtrSoup.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 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 "GRefPtrSoup.h" + +namespace WTF { + +template <> SoupBuffer* refGPtr(SoupBuffer* ptr) +{ + return ptr ? soup_buffer_copy(ptr) : nullptr; +} + +template <> void derefGPtr(SoupBuffer* ptr) +{ + if (ptr) + soup_buffer_free(ptr); +} + +} // namespace WTF diff --git a/Source/WebCore/platform/network/soup/GRefPtrSoup.h b/Source/WebCore/platform/network/soup/GRefPtrSoup.h new file mode 100644 index 000000000..a5988e2fb --- /dev/null +++ b/Source/WebCore/platform/network/soup/GRefPtrSoup.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 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 GRefPtrSoup_h +#define GRefPtrSoup_h + +#if USE(SOUP) + +#include <libsoup/soup.h> +#include <wtf/glib/GRefPtr.h> + +namespace WTF { + +template <> SoupBuffer* refGPtr(SoupBuffer*); +template <> void derefGPtr(SoupBuffer*); + +} // namespace WTF + +#endif // USE(SOUP) + +#endif diff --git a/Source/WebCore/platform/network/soup/GUniquePtrSoup.h b/Source/WebCore/platform/network/soup/GUniquePtrSoup.h index 32775fb46..86dc63741 100644 --- a/Source/WebCore/platform/network/soup/GUniquePtrSoup.h +++ b/Source/WebCore/platform/network/soup/GUniquePtrSoup.h @@ -21,7 +21,7 @@ #define GUniquePtrSoup_h #include <libsoup/soup.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> namespace WTF { diff --git a/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp b/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp index 67f195049..8417691e8 100644 --- a/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp +++ b/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2013 Apple Inc. All rights reserved. * Copyright (C) 2013 University of Szeged. All rights reserved. + * Copyright (C) 2016 Igalia S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,21 +28,39 @@ #include "config.h" #include "NetworkStorageSession.h" +#if USE(SOUP) + #include "ResourceHandle.h" #include "SoupNetworkSession.h" +#include <libsoup/soup.h> #include <wtf/MainThread.h> #include <wtf/NeverDestroyed.h> +#include <wtf/glib/GUniquePtr.h> + +#if USE(LIBSECRET) +#include "GRefPtrGtk.h" +#include <glib/gi18n-lib.h> +#define SECRET_WITH_UNSTABLE 1 +#define SECRET_API_SUBJECT_TO_CHANGE 1 +#include <libsecret/secret.h> +#endif namespace WebCore { -NetworkStorageSession::NetworkStorageSession(std::unique_ptr<SoupNetworkSession> session) - : m_session(std::move(session)) - , m_isPrivate(false) +NetworkStorageSession::NetworkStorageSession(SessionID sessionID, std::unique_ptr<SoupNetworkSession>&& session) + : m_sessionID(sessionID) + , m_session(WTFMove(session)) { + setCookieStorage(m_session ? m_session->cookieJar() : nullptr); } NetworkStorageSession::~NetworkStorageSession() { + g_signal_handlers_disconnect_matched(m_cookieStorage.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); + +#if USE(LIBSECRET) + g_cancellable_cancel(m_persisentStorageCancellable.get()); +#endif } static std::unique_ptr<NetworkStorageSession>& defaultSession() @@ -54,30 +73,207 @@ static std::unique_ptr<NetworkStorageSession>& defaultSession() NetworkStorageSession& NetworkStorageSession::defaultStorageSession() { if (!defaultSession()) - defaultSession() = std::make_unique<NetworkStorageSession>(nullptr); + defaultSession() = std::make_unique<NetworkStorageSession>(SessionID::defaultSessionID(), nullptr); return *defaultSession(); } -std::unique_ptr<NetworkStorageSession> NetworkStorageSession::createPrivateBrowsingSession(const String&) +void NetworkStorageSession::ensurePrivateBrowsingSession(SessionID sessionID, const String&) { - auto session = std::make_unique<NetworkStorageSession>(SoupNetworkSession::createPrivateBrowsingSession()); - session->m_isPrivate = true; - return session; + ASSERT(sessionID != SessionID::defaultSessionID()); + ASSERT(!globalSessionMap().contains(sessionID)); + globalSessionMap().add(sessionID, std::make_unique<NetworkStorageSession>(sessionID, std::make_unique<SoupNetworkSession>())); } void NetworkStorageSession::switchToNewTestingSession() { - defaultSession() = std::make_unique<NetworkStorageSession>(SoupNetworkSession::createTestingSession()); + defaultSession() = std::make_unique<NetworkStorageSession>(SessionID::defaultSessionID(), std::make_unique<SoupNetworkSession>()); +} + +SoupNetworkSession& NetworkStorageSession::getOrCreateSoupNetworkSession() const +{ + if (!m_session) + m_session = std::make_unique<SoupNetworkSession>(m_cookieStorage.get()); + return *m_session; +} + +void NetworkStorageSession::cookiesDidChange(NetworkStorageSession* session) +{ + if (session->m_cookieObserverHandler) + session->m_cookieObserverHandler(); +} + +SoupCookieJar* NetworkStorageSession::cookieStorage() const +{ + RELEASE_ASSERT(!m_session || m_session->cookieJar() == m_cookieStorage.get()); + return m_cookieStorage.get(); +} + +void NetworkStorageSession::setCookieStorage(SoupCookieJar* jar) +{ + if (m_cookieStorage) + g_signal_handlers_disconnect_matched(m_cookieStorage.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); + + // We always have a valid cookieStorage. + if (jar) + m_cookieStorage = jar; + else { + m_cookieStorage = adoptGRef(soup_cookie_jar_new()); + soup_cookie_jar_set_accept_policy(m_cookieStorage.get(), SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); + } + g_signal_connect_swapped(m_cookieStorage.get(), "changed", G_CALLBACK(cookiesDidChange), this); + if (m_session && m_session->cookieJar() != m_cookieStorage.get()) + m_session->setCookieJar(m_cookieStorage.get()); +} + +void NetworkStorageSession::setCookieObserverHandler(Function<void ()>&& handler) +{ + m_cookieObserverHandler = WTFMove(handler); } -SoupNetworkSession& NetworkStorageSession::soupNetworkSession() const +#if USE(LIBSECRET) +static const char* schemeFromProtectionSpaceServerType(ProtectionSpaceServerType serverType) { - return m_session ? *m_session : SoupNetworkSession::defaultSession(); + switch (serverType) { + case ProtectionSpaceServerHTTP: + case ProtectionSpaceProxyHTTP: + return SOUP_URI_SCHEME_HTTP; + case ProtectionSpaceServerHTTPS: + case ProtectionSpaceProxyHTTPS: + return SOUP_URI_SCHEME_HTTPS; + case ProtectionSpaceServerFTP: + case ProtectionSpaceProxyFTP: + return SOUP_URI_SCHEME_FTP; + case ProtectionSpaceServerFTPS: + case ProtectionSpaceProxySOCKS: + break; + } + + ASSERT_NOT_REACHED(); + return SOUP_URI_SCHEME_HTTP; } -void NetworkStorageSession::setSoupNetworkSession(std::unique_ptr<SoupNetworkSession> session) +static const char* authTypeFromProtectionSpaceAuthenticationScheme(ProtectionSpaceAuthenticationScheme scheme) { - m_session = std::move(session); + switch (scheme) { + case ProtectionSpaceAuthenticationSchemeDefault: + case ProtectionSpaceAuthenticationSchemeHTTPBasic: + return "Basic"; + case ProtectionSpaceAuthenticationSchemeHTTPDigest: + return "Digest"; + case ProtectionSpaceAuthenticationSchemeNTLM: + return "NTLM"; + case ProtectionSpaceAuthenticationSchemeNegotiate: + return "Negotiate"; + case ProtectionSpaceAuthenticationSchemeHTMLForm: + case ProtectionSpaceAuthenticationSchemeClientCertificateRequested: + case ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested: + ASSERT_NOT_REACHED(); + break; + case ProtectionSpaceAuthenticationSchemeUnknown: + return "unknown"; + } + + ASSERT_NOT_REACHED(); + return "unknown"; } +#endif // USE(LIBSECRET) + +void NetworkStorageSession::getCredentialFromPersistentStorage(const ProtectionSpace& protectionSpace, Function<void (Credential&&)> completionHandler) +{ +#if USE(LIBSECRET) + if (m_sessionID.isEphemeral()) { + completionHandler({ }); + return; + } + + const String& realm = protectionSpace.realm(); + if (realm.isEmpty()) { + completionHandler({ }); + return; + } + + GRefPtr<GHashTable> attributes = adoptGRef(secret_attributes_build(SECRET_SCHEMA_COMPAT_NETWORK, + "domain", realm.utf8().data(), + "server", protectionSpace.host().utf8().data(), + "port", protectionSpace.port(), + "protocol", schemeFromProtectionSpaceServerType(protectionSpace.serverType()), + "authtype", authTypeFromProtectionSpaceAuthenticationScheme(protectionSpace.authenticationScheme()), + nullptr)); + if (!attributes) { + completionHandler({ }); + return; + } + + m_persisentStorageCancellable = adoptGRef(g_cancellable_new()); + m_persisentStorageCompletionHandler = WTFMove(completionHandler); + secret_service_search(nullptr, SECRET_SCHEMA_COMPAT_NETWORK, attributes.get(), + static_cast<SecretSearchFlags>(SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS), m_persisentStorageCancellable.get(), + [](GObject* source, GAsyncResult* result, gpointer userData) { + GUniqueOutPtr<GError> error; + GUniquePtr<GList> elements(secret_service_search_finish(SECRET_SERVICE(source), result, &error.outPtr())); + if (g_error_matches (error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + NetworkStorageSession* session = static_cast<NetworkStorageSession*>(userData); + auto completionHandler = std::exchange(session->m_persisentStorageCompletionHandler, nullptr); + if (error || !elements || !elements->data) { + completionHandler({ }); + return; + } + + GRefPtr<SecretItem> secretItem = adoptGRef(static_cast<SecretItem*>(elements->data)); + GRefPtr<GHashTable> attributes = adoptGRef(secret_item_get_attributes(secretItem.get())); + String user = String::fromUTF8(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "user"))); + if (user.isEmpty()) { + completionHandler({ }); + return; + } + size_t length; + GRefPtr<SecretValue> secretValue = adoptGRef(secret_item_get_secret(secretItem.get())); + const char* passwordData = secret_value_get(secretValue.get(), &length); + completionHandler(Credential(user, String::fromUTF8(passwordData, length), CredentialPersistencePermanent)); + }, this); +#else + UNUSED_PARAM(protectionSpace); + completionHandler({ }); +#endif } + +void NetworkStorageSession::saveCredentialToPersistentStorage(const ProtectionSpace& protectionSpace, const Credential& credential) +{ +#if USE(LIBSECRET) + if (m_sessionID.isEphemeral()) + return; + + if (credential.isEmpty()) + return; + + const String& realm = protectionSpace.realm(); + if (realm.isEmpty()) + return; + + GRefPtr<GHashTable> attributes = adoptGRef(secret_attributes_build(SECRET_SCHEMA_COMPAT_NETWORK, + "domain", realm.utf8().data(), + "server", protectionSpace.host().utf8().data(), + "port", protectionSpace.port(), + "protocol", schemeFromProtectionSpaceServerType(protectionSpace.serverType()), + "authtype", authTypeFromProtectionSpaceAuthenticationScheme(protectionSpace.authenticationScheme()), + nullptr)); + if (!attributes) + return; + + g_hash_table_insert(attributes.get(), g_strdup("user"), g_strdup(credential.user().utf8().data())); + CString utf8Password = credential.password().utf8(); + GRefPtr<SecretValue> newSecretValue = adoptGRef(secret_value_new(utf8Password.data(), utf8Password.length(), "text/plain")); + secret_service_store(nullptr, SECRET_SCHEMA_COMPAT_NETWORK, attributes.get(), SECRET_COLLECTION_DEFAULT, _("WebKitGTK+ password"), + newSecretValue.get(), nullptr, nullptr, nullptr); +#else + UNUSED_PARAM(protectionSpace); + UNUSED_PARAM(credential); +#endif +} + +} // namespace WebCore + +#endif // USE(SOUP) diff --git a/Source/WebCore/platform/network/soup/ProxyServerSoup.cpp b/Source/WebCore/platform/network/soup/ProxyServerSoup.cpp index c85fe6891..4be57ca66 100644 --- a/Source/WebCore/platform/network/soup/ProxyServerSoup.cpp +++ b/Source/WebCore/platform/network/soup/ProxyServerSoup.cpp @@ -26,6 +26,8 @@ #include "config.h" #include "ProxyServer.h" +#if USE(SOUP) + #include "URL.h" namespace WebCore { @@ -37,3 +39,5 @@ Vector<ProxyServer> proxyServersForURL(const URL&, const NetworkingContext*) } } // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/ResourceError.h b/Source/WebCore/platform/network/soup/ResourceError.h index 1a705f15e..bf0291848 100644 --- a/Source/WebCore/platform/network/soup/ResourceError.h +++ b/Source/WebCore/platform/network/soup/ResourceError.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -27,7 +27,10 @@ #define ResourceError_h #include "ResourceErrorBase.h" -#include <wtf/gobject/GRefPtr.h> + +#if USE(SOUP) + +#include <wtf/glib/GRefPtr.h> typedef struct _GTlsCertificate GTlsCertificate; typedef struct _SoupRequest SoupRequest; @@ -38,14 +41,15 @@ namespace WebCore { class ResourceError : public ResourceErrorBase { public: - ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) - : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) + ResourceError(Type type = Type::Null) + : ResourceErrorBase(type) , m_tlsErrors(0) { } - ResourceError() - : m_tlsErrors(0) + ResourceError(const String& domain, int errorCode, const URL& failingURL, const String& localizedDescription, Type type = Type::General) + : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription, type) + , m_tlsErrors(0) { } @@ -53,7 +57,7 @@ public: static ResourceError transportError(SoupRequest*, int statusCode, const String& reasonPhrase); static ResourceError genericGError(GError*, SoupRequest*); static ResourceError tlsError(SoupRequest*, unsigned tlsErrors, GTlsCertificate*); - static ResourceError timeoutError(const String& failingURL); + static ResourceError timeoutError(const URL& failingURL); static ResourceError authenticationError(SoupMessage*); unsigned tlsErrors() const { return m_tlsErrors; } @@ -61,14 +65,18 @@ public: GTlsCertificate* certificate() const { return m_certificate.get(); } void setCertificate(GTlsCertificate* certificate) { m_certificate = certificate; } -private: - void platformCopy(ResourceError&) const; static bool platformCompare(const ResourceError& a, const ResourceError& b); +private: + friend class ResourceErrorBase; + void doPlatformIsolatedCopy(const ResourceError&); + unsigned m_tlsErrors; GRefPtr<GTlsCertificate> m_certificate; }; } +#endif + #endif // ResourceError_h_ diff --git a/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp b/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp index bb72c64c4..a608e7e10 100644 --- a/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY IGALIA S.L. ``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 + * 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 @@ -26,21 +26,22 @@ #include "config.h" #include "ResourceError.h" +#if USE(SOUP) + #include "LocalizedStrings.h" #include <libsoup/soup.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> namespace WebCore { -static String failingURI(SoupURI* soupURI) +static URL failingURI(SoupURI* soupURI) { ASSERT(soupURI); - GUniquePtr<char> uri(soup_uri_to_string(soupURI, FALSE)); - return uri.get(); + return URL(soupURI); } -static String failingURI(SoupRequest* request) +static URL failingURI(SoupRequest* request) { ASSERT(request); return failingURI(soup_request_get_uri(request)); @@ -83,23 +84,21 @@ ResourceError ResourceError::tlsError(SoupRequest* request, unsigned tlsErrors, return resourceError; } -ResourceError ResourceError::timeoutError(const String& failingURL) +ResourceError ResourceError::timeoutError(const URL& failingURL) { - // FIXME: This should probably either be integrated into Errors(Gtk/EFL).h or the - // networking errors from those files should be moved here. + // FIXME: This should probably either be integrated into ErrorsGtk.h or the + // networking errors from that file should be moved here. // Use the same value as in NSURLError.h static const int timeoutError = -1001; static const char* const errorDomain = "WebKitNetworkError"; - ResourceError error = ResourceError(errorDomain, timeoutError, failingURL, "Request timed out"); - error.setIsTimeout(true); - return error; + return ResourceError(errorDomain, timeoutError, failingURL, "Request timed out", ResourceError::Type::Timeout); } -void ResourceError::platformCopy(ResourceError& errorCopy) const +void ResourceError::doPlatformIsolatedCopy(const ResourceError& other) { - errorCopy.m_certificate = m_certificate; - errorCopy.m_tlsErrors = m_tlsErrors; + m_certificate = other.m_certificate; + m_tlsErrors = other.m_tlsErrors; } bool ResourceError::platformCompare(const ResourceError& a, const ResourceError& b) @@ -108,3 +107,5 @@ bool ResourceError::platformCompare(const ResourceError& a, const ResourceError& } } // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp index dffa31eaa..43be3e6a5 100644 --- a/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -29,15 +29,16 @@ #include "config.h" #include "ResourceHandle.h" -#include "CookieJarSoup.h" +#if USE(SOUP) + #include "CredentialStorage.h" #include "FileSystem.h" #include "GUniquePtrSoup.h" #include "HTTPParsers.h" #include "LocalizedStrings.h" #include "MIMETypeRegistry.h" +#include "NetworkStorageSession.h" #include "NetworkingContext.h" -#include "NotImplemented.h" #include "ResourceError.h" #include "ResourceHandleClient.h" #include "ResourceHandleInternal.h" @@ -52,205 +53,26 @@ #include <libsoup/soup.h> #include <sys/stat.h> #include <sys/types.h> +#if !COMPILER(MSVC) #include <unistd.h> +#endif #include <wtf/CurrentTime.h> -#include <wtf/SHA1.h> -#include <wtf/gobject/GRefPtr.h> -#include <wtf/text/Base64.h> +#include <wtf/glib/GRefPtr.h> #include <wtf/text/CString.h> -#if ENABLE(BLOB) -#include "BlobData.h" -#include "BlobRegistryImpl.h" -#include "BlobStorageData.h" -#endif - -#if PLATFORM(GTK) -#include "CredentialBackingStore.h" -#endif - namespace WebCore { -static bool loadingSynchronousRequest = false; static const size_t gDefaultReadBufferSize = 8192; -class WebCoreSynchronousLoader final : public ResourceHandleClient { - WTF_MAKE_NONCOPYABLE(WebCoreSynchronousLoader); -public: - - WebCoreSynchronousLoader(ResourceError& error, ResourceResponse& response, SoupSession* session, Vector<char>& data, StoredCredentials storedCredentials) - : m_error(error) - , m_response(response) - , m_session(session) - , m_data(data) - , m_finished(false) - , m_storedCredentials(storedCredentials) - { - // We don't want any timers to fire while we are doing our synchronous load - // so we replace the thread default main context. The main loop iterations - // will only process GSources associated with this inner context. - loadingSynchronousRequest = true; - GRefPtr<GMainContext> innerMainContext = adoptGRef(g_main_context_new()); - g_main_context_push_thread_default(innerMainContext.get()); - m_mainLoop = adoptGRef(g_main_loop_new(innerMainContext.get(), false)); - -#if !SOUP_CHECK_VERSION(2, 49, 91) - adjustMaxConnections(1); -#endif - } - - ~WebCoreSynchronousLoader() - { -#if !SOUP_CHECK_VERSION(2, 49, 91) - adjustMaxConnections(-1); -#endif - - GMainContext* context = g_main_context_get_thread_default(); - while (g_main_context_pending(context)) - g_main_context_iteration(context, FALSE); - - g_main_context_pop_thread_default(context); - loadingSynchronousRequest = false; - } - -#if !SOUP_CHECK_VERSION(2, 49, 91) - void adjustMaxConnections(int adjustment) - { - int maxConnections, maxConnectionsPerHost; - g_object_get(m_session, - SOUP_SESSION_MAX_CONNS, &maxConnections, - SOUP_SESSION_MAX_CONNS_PER_HOST, &maxConnectionsPerHost, - NULL); - maxConnections += adjustment; - maxConnectionsPerHost += adjustment; - g_object_set(m_session, - SOUP_SESSION_MAX_CONNS, maxConnections, - SOUP_SESSION_MAX_CONNS_PER_HOST, maxConnectionsPerHost, - NULL); - - } -#endif // SOUP_CHECK_VERSION(2, 49, 91) - - virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse& response) override - { - m_response = response; - } - - virtual void didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int) override - { - ASSERT_NOT_REACHED(); - } - - virtual void didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer> buffer, int /* encodedLength */) override - { - // This pattern is suggested by SharedBuffer.h. - const char* segment; - unsigned position = 0; - while (unsigned length = buffer->getSomeData(segment, position)) { - m_data.append(segment, length); - position += length; - } - } - - virtual void didFinishLoading(ResourceHandle*, double) override - { - if (g_main_loop_is_running(m_mainLoop.get())) - g_main_loop_quit(m_mainLoop.get()); - m_finished = true; - } - - virtual void didFail(ResourceHandle* handle, const ResourceError& error) override - { - m_error = error; - didFinishLoading(handle, 0); - } - - virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) override - { - // We do not handle authentication for synchronous XMLHttpRequests. - challenge.authenticationClient()->receivedRequestToContinueWithoutCredential(challenge); - } - - virtual bool shouldUseCredentialStorage(ResourceHandle*) override - { - return m_storedCredentials == AllowStoredCredentials; - } - - void run() - { - if (!m_finished) - g_main_loop_run(m_mainLoop.get()); - } - -private: - ResourceError& m_error; - ResourceResponse& m_response; - SoupSession* m_session; - Vector<char>& m_data; - bool m_finished; - GRefPtr<GMainLoop> m_mainLoop; - StoredCredentials m_storedCredentials; -}; - -class HostTLSCertificateSet { -public: - void add(GTlsCertificate* certificate) - { - String certificateHash = computeCertificateHash(certificate); - if (!certificateHash.isEmpty()) - m_certificates.add(certificateHash); - } - - bool contains(GTlsCertificate* certificate) - { - return m_certificates.contains(computeCertificateHash(certificate)); - } - -private: - static String computeCertificateHash(GTlsCertificate* certificate) - { - GRefPtr<GByteArray> certificateData; - g_object_get(G_OBJECT(certificate), "certificate", &certificateData.outPtr(), NULL); - if (!certificateData) - return String(); - - SHA1 sha1; - sha1.addBytes(certificateData->data, certificateData->len); - - SHA1::Digest digest; - sha1.computeHash(digest); - - return base64Encode(reinterpret_cast<const char*>(digest.data()), SHA1::hashSize); - } - - HashSet<String> m_certificates; -}; - -static bool createSoupRequestAndMessageForHandle(ResourceHandle*, const ResourceRequest&, bool isHTTPFamilyRequest); +static bool createSoupRequestAndMessageForHandle(ResourceHandle*, const ResourceRequest&); static void cleanupSoupRequestOperation(ResourceHandle*, bool isDestroying = false); static void sendRequestCallback(GObject*, GAsyncResult*, gpointer); static void readCallback(GObject*, GAsyncResult*, gpointer); -static gboolean requestTimeoutCallback(void*); #if ENABLE(WEB_TIMING) -static int milisecondsSinceRequest(double requestTime); +static double milisecondsSinceRequest(double requestTime); #endif static void continueAfterDidReceiveResponse(ResourceHandle*); -static bool gIgnoreSSLErrors = false; - -static HashSet<String>& allowsAnyHTTPSCertificateHosts() -{ - DEFINE_STATIC_LOCAL(HashSet<String>, hosts, ()); - return hosts; -} - -typedef HashMap<String, HostTLSCertificateSet> CertificatesMap; -static CertificatesMap& clientCertificates() -{ - DEFINE_STATIC_LOCAL(CertificatesMap, certificates, ()); - return certificates; -} - ResourceHandleInternal::~ResourceHandleInternal() { } @@ -258,8 +80,8 @@ ResourceHandleInternal::~ResourceHandleInternal() static SoupSession* sessionFromContext(NetworkingContext* context) { if (!context || !context->isValid()) - return SoupNetworkSession::defaultSession().soupSession(); - return context->storageSession().soupNetworkSession().soupSession(); + return NetworkStorageSession::defaultStorageSession().getOrCreateSoupNetworkSession().soupSession(); + return context->storageSession().getOrCreateSoupNetworkSession().soupSession(); } ResourceHandle::~ResourceHandle() @@ -269,7 +91,36 @@ ResourceHandle::~ResourceHandle() SoupSession* ResourceHandleInternal::soupSession() { - return sessionFromContext(m_context.get()); + return m_session ? m_session->soupSession() : sessionFromContext(m_context.get()); +} + +RefPtr<ResourceHandle> ResourceHandle::create(SoupNetworkSession& session, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) +{ + auto newHandle = adoptRef(*new ResourceHandle(session, request, client, defersLoading, shouldContentSniff)); + + if (newHandle->d->m_scheduledFailureType != NoFailure) + return WTFMove(newHandle); + + if (newHandle->start()) + return WTFMove(newHandle); + + return nullptr; +} + +ResourceHandle::ResourceHandle(SoupNetworkSession& session, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) + : d(std::make_unique<ResourceHandleInternal>(this, nullptr, request, client, defersLoading, shouldContentSniff && shouldContentSniffURL(request.url()))) +{ + if (!request.url().isValid()) { + scheduleFailure(InvalidURLFailure); + return; + } + + if (!portAllowed(request.url())) { + scheduleFailure(BlockedFailure); + return; + } + + d->m_session = &session; } bool ResourceHandle::cancelledOrClientless() @@ -305,39 +156,19 @@ static bool isAuthenticationFailureStatusCode(int httpStatusCode) return httpStatusCode == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED || httpStatusCode == SOUP_STATUS_UNAUTHORIZED; } -static bool handleUnignoredTLSErrors(ResourceHandle* handle, SoupMessage* message) -{ - if (gIgnoreSSLErrors) - return false; - - GTlsCertificate* certificate = nullptr; - GTlsCertificateFlags tlsErrors = static_cast<GTlsCertificateFlags>(0); - soup_message_get_https_status(message, &certificate, &tlsErrors); - if (!tlsErrors) - return false; - - String lowercaseHostURL = handle->firstRequest().url().host().lower(); - if (allowsAnyHTTPSCertificateHosts().contains(lowercaseHostURL)) - return false; - - // We aren't ignoring errors globally, but the user may have already decided to accept this certificate. - auto it = clientCertificates().find(lowercaseHostURL); - if (it != clientCertificates().end() && it->value.contains(certificate)) - return false; - - ResourceHandleInternal* d = handle->getInternal(); - handle->client()->didFail(handle, ResourceError::tlsError(d->m_soupRequest.get(), tlsErrors, certificate)); - return true; -} - static void tlsErrorsChangedCallback(SoupMessage* message, GParamSpec*, gpointer data) { - ResourceHandle* handle = static_cast<ResourceHandle*>(data); + RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); if (!handle || handle->cancelledOrClientless()) return; - if (handleUnignoredTLSErrors(handle, message)) + SoupNetworkSession::checkTLSErrors(handle->getInternal()->m_soupRequest.get(), message, [handle] (const ResourceError& error) { + if (error.isNull()) + return; + + handle->client()->didFail(handle.get(), error); handle->cancel(); + }); } static void gotHeadersCallback(SoupMessage* message, gpointer data) @@ -348,22 +179,17 @@ static void gotHeadersCallback(SoupMessage* message, gpointer data) ResourceHandleInternal* d = handle->getInternal(); -#if ENABLE(WEB_TIMING) - if (d->m_response.resourceLoadTiming()) - d->m_response.resourceLoadTiming()->receiveHeadersEnd = milisecondsSinceRequest(d->m_response.resourceLoadTiming()->requestTime); -#endif - -#if PLATFORM(GTK) - // We are a bit more conservative with the persistent credential storage than the session store, - // since we are waiting until we know that this authentication succeeded before actually storing. - // This is because we want to avoid hitting the disk twice (once to add and once to remove) for - // incorrect credentials or polluting the keychain with invalid credentials. - if (!isAuthenticationFailureStatusCode(message->status_code) && message->status_code < 500 && !d->m_credentialDataToSaveInPersistentStore.credential.isEmpty()) { - credentialBackingStore().storeCredentialsForChallenge( - d->m_credentialDataToSaveInPersistentStore.challenge, - d->m_credentialDataToSaveInPersistentStore.credential); + if (d->m_context && d->m_context->isValid()) { + // We are a bit more conservative with the persistent credential storage than the session store, + // since we are waiting until we know that this authentication succeeded before actually storing. + // This is because we want to avoid hitting the disk twice (once to add and once to remove) for + // incorrect credentials or polluting the keychain with invalid credentials. + if (!isAuthenticationFailureStatusCode(message->status_code) && message->status_code < 500) { + d->m_context->storageSession().saveCredentialToPersistentStorage( + d->m_credentialDataToSaveInPersistentStore.protectionSpace, + d->m_credentialDataToSaveInPersistentStore.credential); + } } -#endif // The original response will be needed later to feed to willSendRequest in // doRedirect() in case we are redirected. For this reason, we store it here. @@ -375,15 +201,17 @@ static void applyAuthenticationToRequest(ResourceHandle* handle, ResourceRequest // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open(). ResourceHandleInternal* d = handle->getInternal(); + String partition = request.cachePartition(); + if (handle->shouldUseCredentialStorage()) { if (d->m_user.isEmpty() && d->m_pass.isEmpty()) - d->m_initialCredential = CredentialStorage::get(request.url()); + d->m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, request.url()); else if (!redirect) { // 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), request.url()); + CredentialStorage::defaultCredentialStorage().set(partition, Credential(d->m_user, d->m_pass, CredentialPersistenceNone), request.url()); } } @@ -419,10 +247,7 @@ static void restartedCallback(SoupMessage*, gpointer data) if (!handle || handle->cancelledOrClientless()) return; - ResourceHandleInternal* d = handle->getInternal(); - ResourceResponse& redirectResponse = d->m_response; - redirectResponse.setResourceLoadTiming(ResourceLoadTiming::create()); - redirectResponse.resourceLoadTiming()->requestTime = monotonicallyIncreasingTime(); + handle->m_requestTime = monotonicallyIncreasingTime(); } #endif @@ -465,18 +290,17 @@ static bool shouldRedirectAsGET(SoupMessage* message, URL& newURL, bool crossOri return false; } -static void continueAfterWillSendRequest(ResourceHandle* handle, const ResourceRequest& request) +static void continueAfterWillSendRequest(ResourceHandle* handle, ResourceRequest&& request) { // willSendRequest might cancel the load. if (handle->cancelledOrClientless()) return; - ResourceRequest newRequest(request); ResourceHandleInternal* d = handle->getInternal(); - if (protocolHostAndPortAreEqual(newRequest.url(), d->m_response.url())) - applyAuthenticationToRequest(handle, newRequest, true); + if (protocolHostAndPortAreEqual(request.url(), d->m_response.url())) + applyAuthenticationToRequest(handle, request, true); - if (!createSoupRequestAndMessageForHandle(handle, newRequest, true)) { + if (!createSoupRequestAndMessageForHandle(handle, request)) { d->client()->cannotShowURL(handle); return; } @@ -501,14 +325,13 @@ static void doRedirect(ResourceHandle* handle) URL newURL = URL(URL(soup_message_get_uri(message)), location); bool crossOrigin = !protocolHostAndPortAreEqual(handle->firstRequest().url(), newURL); newRequest.setURL(newURL); - newRequest.setFirstPartyForCookies(newURL); if (newRequest.httpMethod() != "GET") { // Change newRequest method to GET if change was made during a previous redirection // or if current redirection says so if (message->method == SOUP_METHOD_GET || shouldRedirectAsGET(message, newURL, crossOrigin)) { newRequest.setHTTPMethod("GET"); - newRequest.setHTTPBody(0); + newRequest.setHTTPBody(nullptr); newRequest.clearHTTPContentType(); } } @@ -533,11 +356,12 @@ static void doRedirect(ResourceHandle* handle) cleanupSoupRequestOperation(handle); + ResourceResponse responseCopy = d->m_response; if (d->client()->usesAsyncCallbacks()) - d->client()->willSendRequestAsync(handle, newRequest, d->m_response); + d->client()->willSendRequestAsync(handle, WTFMove(newRequest), WTFMove(responseCopy)); else { - d->client()->willSendRequest(handle, newRequest, d->m_response); - continueAfterWillSendRequest(handle, newRequest); + auto request = d->client()->willSendRequest(handle, WTFMove(newRequest), WTFMove(responseCopy)); + continueAfterWillSendRequest(handle, WTFMove(request)); } } @@ -603,10 +427,7 @@ static void cleanupSoupRequestOperation(ResourceHandle* handle, bool isDestroyin d->m_soupMessage.clear(); } - if (d->m_timeoutSource) { - g_source_destroy(d->m_timeoutSource.get()); - d->m_timeoutSource.clear(); - } + d->m_timeoutSource.stop(); if (!isDestroying) handle->deref(); @@ -657,12 +478,7 @@ static void nextMultipartResponsePartCallback(GObject* /*source*/, GAsyncResult* d->m_previousPosition = 0; - if (handle->client()->usesAsyncCallbacks()) - handle->client()->didReceiveResponseAsync(handle.get(), d->m_response); - else { - handle->client()->didReceiveResponse(handle.get(), d->m_response); - continueAfterDidReceiveResponse(handle.get()); - } + handle->didReceiveResponse(ResourceResponse(d->m_response)); } static void sendRequestCallback(GObject*, GAsyncResult* result, gpointer data) @@ -712,17 +528,21 @@ static void sendRequestCallback(GObject*, GAsyncResult* result, gpointer data) d->m_response.setExpectedContentLength(soup_request_get_content_length(d->m_soupRequest.get())); } +#if ENABLE(WEB_TIMING) + d->m_response.networkLoadTiming().responseStart = milisecondsSinceRequest(handle->m_requestTime); +#endif + if (soupMessage && d->m_response.isMultipart()) d->m_multipartInputStream = adoptGRef(soup_multipart_input_stream_new(soupMessage, inputStream.get())); else d->m_inputStream = inputStream; - if (d->client()->usesAsyncCallbacks()) - handle->client()->didReceiveResponseAsync(handle.get(), d->m_response); - else { - handle->client()->didReceiveResponse(handle.get(), d->m_response); - continueAfterDidReceiveResponse(handle.get()); - } + handle->didReceiveResponse(ResourceResponse(d->m_response)); +} + +void ResourceHandle::platformContinueSynchronousDidReceiveResponse() +{ + continueAfterDidReceiveResponse(this); } static void continueAfterDidReceiveResponse(ResourceHandle* handle) @@ -745,141 +565,23 @@ static void continueAfterDidReceiveResponse(ResourceHandle* handle) G_PRIORITY_DEFAULT, d->m_cancellable.get(), readCallback, handle); } -static bool addFileToSoupMessageBody(SoupMessage* message, const String& fileNameString, size_t offset, size_t lengthToSend, unsigned long& totalBodySize) -{ - GUniqueOutPtr<GError> error; - CString fileName = fileSystemRepresentation(fileNameString); - GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr()); - if (error) - return false; - - gsize bufferLength = lengthToSend; - if (!lengthToSend) - bufferLength = g_mapped_file_get_length(fileMapping); - totalBodySize += bufferLength; - - SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping) + offset, - bufferLength, - fileMapping, - reinterpret_cast<GDestroyNotify>(g_mapped_file_unref)); - soup_message_body_append_buffer(message->request_body, soupBuffer); - soup_buffer_free(soupBuffer); - return true; -} - -#if ENABLE(BLOB) -static bool blobIsOutOfDate(const BlobDataItem& blobItem) -{ - ASSERT(blobItem.type == BlobDataItem::File); - if (!isValidFileTime(blobItem.expectedModificationTime)) - return false; - - time_t fileModificationTime; - if (!getFileModificationTime(blobItem.path, fileModificationTime)) - return true; - - return fileModificationTime != static_cast<time_t>(blobItem.expectedModificationTime); -} - -static void addEncodedBlobItemToSoupMessageBody(SoupMessage* message, const BlobDataItem& blobItem, unsigned long& totalBodySize) -{ - if (blobItem.type == BlobDataItem::Data) { - totalBodySize += blobItem.length; - soup_message_body_append(message->request_body, SOUP_MEMORY_TEMPORARY, - blobItem.data->data() + blobItem.offset, blobItem.length); - return; - } - - ASSERT(blobItem.type == BlobDataItem::File); - if (blobIsOutOfDate(blobItem)) - return; - - addFileToSoupMessageBody(message, - blobItem.path, - blobItem.offset, - blobItem.length == BlobDataItem::toEndOfFile ? 0 : blobItem.length, - totalBodySize); -} - -static void addEncodedBlobToSoupMessageBody(SoupMessage* message, const FormDataElement& element, unsigned long& totalBodySize) -{ - RefPtr<BlobStorageData> blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(URL(ParsedURLString, element.m_url)); - if (!blobData) - return; - - for (size_t i = 0; i < blobData->items().size(); ++i) - addEncodedBlobItemToSoupMessageBody(message, blobData->items()[i], totalBodySize); -} -#endif // ENABLE(BLOB) - -static bool addFormElementsToSoupMessage(SoupMessage* message, const char*, FormData* httpBody, unsigned long& totalBodySize) -{ - soup_message_body_set_accumulate(message->request_body, FALSE); - size_t numElements = httpBody->elements().size(); - for (size_t i = 0; i < numElements; i++) { - const FormDataElement& element = httpBody->elements()[i]; - - if (element.m_type == FormDataElement::data) { - totalBodySize += element.m_data.size(); - soup_message_body_append(message->request_body, SOUP_MEMORY_TEMPORARY, - element.m_data.data(), element.m_data.size()); - continue; - } - - if (element.m_type == FormDataElement::encodedFile) { - if (!addFileToSoupMessageBody(message , - element.m_filename, - 0 /* offset */, - 0 /* lengthToSend */, - totalBodySize)) - return false; - continue; - } - -#if ENABLE(BLOB) - ASSERT(element.m_type == FormDataElement::encodedBlob); - addEncodedBlobToSoupMessageBody(message, element, totalBodySize); -#endif - } - return true; -} - #if ENABLE(WEB_TIMING) -static int milisecondsSinceRequest(double requestTime) +static double milisecondsSinceRequest(double requestTime) { - return static_cast<int>((monotonicallyIncreasingTime() - requestTime) * 1000.0); + return (monotonicallyIncreasingTime() - requestTime) * 1000.0; } -static void wroteBodyCallback(SoupMessage*, gpointer data) +void ResourceHandle::didStartRequest() { - RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); - if (!handle) - return; - - ResourceHandleInternal* d = handle->getInternal(); - if (!d->m_response.resourceLoadTiming()) - return; - - d->m_response.resourceLoadTiming()->sendEnd = milisecondsSinceRequest(d->m_response.resourceLoadTiming()->requestTime); + getInternal()->m_response.networkLoadTiming().requestStart = milisecondsSinceRequest(m_requestTime); } -void ResourceHandle::didStartRequest() +#if SOUP_CHECK_VERSION(2, 49, 91) +static void startingCallback(SoupMessage*, ResourceHandle* handle) { - ResourceHandleInternal* d = getInternal(); - if (!d->m_response.resourceLoadTiming()) - return; - - d->m_response.resourceLoadTiming()->sendStart = milisecondsSinceRequest(d->m_response.resourceLoadTiming()->requestTime); - if (d->m_response.resourceLoadTiming()->sslStart != -1) { - // WebCore/inspector/front-end/RequestTimingView.js assumes - // that SSL time is included in connection time so must - // substract here the SSL delta that will be added later (see - // WebInspector.RequestTimingView.createTimingTable in the - // file above for more details). - d->m_response.resourceLoadTiming()->sendStart -= - d->m_response.resourceLoadTiming()->sslEnd - d->m_response.resourceLoadTiming()->sslStart; - } + handle->didStartRequest(); } +#endif // SOUP_CHECK_VERSION(2, 49, 91) static void networkEventCallback(SoupMessage*, GSocketClientEvent event, GIOStream*, gpointer data) { @@ -891,43 +593,41 @@ static void networkEventCallback(SoupMessage*, GSocketClientEvent event, GIOStre return; ResourceHandleInternal* d = handle->getInternal(); - int deltaTime = milisecondsSinceRequest(d->m_response.resourceLoadTiming()->requestTime); + double deltaTime = milisecondsSinceRequest(handle->m_requestTime); switch (event) { case G_SOCKET_CLIENT_RESOLVING: - d->m_response.resourceLoadTiming()->dnsStart = deltaTime; + d->m_response.networkLoadTiming().domainLookupStart = deltaTime; break; case G_SOCKET_CLIENT_RESOLVED: - d->m_response.resourceLoadTiming()->dnsEnd = deltaTime; + d->m_response.networkLoadTiming().domainLookupEnd = deltaTime; break; case G_SOCKET_CLIENT_CONNECTING: - d->m_response.resourceLoadTiming()->connectStart = deltaTime; - if (d->m_response.resourceLoadTiming()->dnsStart != -1) + d->m_response.networkLoadTiming().connectStart = deltaTime; + if (d->m_response.networkLoadTiming().domainLookupStart != -1) { // WebCore/inspector/front-end/RequestTimingView.js assumes // that DNS time is included in connection time so must // substract here the DNS delta that will be added later (see // WebInspector.RequestTimingView.createTimingTable in the // file above for more details). - d->m_response.resourceLoadTiming()->connectStart -= - d->m_response.resourceLoadTiming()->dnsEnd - d->m_response.resourceLoadTiming()->dnsStart; + d->m_response.networkLoadTiming().connectStart -= + d->m_response.networkLoadTiming().domainLookupEnd - d->m_response.networkLoadTiming().domainLookupStart; + } break; case G_SOCKET_CLIENT_CONNECTED: // Web Timing considers that connection time involves dns, proxy & TLS negotiation... // so we better pick G_SOCKET_CLIENT_COMPLETE for connectEnd break; case G_SOCKET_CLIENT_PROXY_NEGOTIATING: - d->m_response.resourceLoadTiming()->proxyStart = deltaTime; break; case G_SOCKET_CLIENT_PROXY_NEGOTIATED: - d->m_response.resourceLoadTiming()->proxyEnd = deltaTime; break; case G_SOCKET_CLIENT_TLS_HANDSHAKING: - d->m_response.resourceLoadTiming()->sslStart = deltaTime; + d->m_response.networkLoadTiming().secureConnectionStart = deltaTime; break; case G_SOCKET_CLIENT_TLS_HANDSHAKED: - d->m_response.resourceLoadTiming()->sslEnd = deltaTime; break; case G_SOCKET_CLIENT_COMPLETE: - d->m_response.resourceLoadTiming()->connectEnd = deltaTime; + d->m_response.networkLoadTiming().connectEnd = deltaTime; break; default: ASSERT_NOT_REACHED(); @@ -949,6 +649,7 @@ static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const R SoupMessage* soupMessage = d->m_soupMessage.get(); request.updateSoupMessage(soupMessage); + d->m_bodySize = soupMessage->request_body->length; g_object_set_data(G_OBJECT(soupMessage), "handle", handle); if (!handle->shouldContentSniff()) @@ -956,14 +657,6 @@ static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const R if (!d->m_useAuthenticationManager) soup_message_disable_feature(soupMessage, SOUP_TYPE_AUTH_MANAGER); - FormData* httpBody = request.httpBody(); - CString contentType = request.httpContentType().utf8().data(); - if (httpBody && !httpBody->isEmpty() && !addFormElementsToSoupMessage(soupMessage, contentType.data(), httpBody, d->m_bodySize)) { - // We failed to prepare the body data, so just fail this load. - d->m_soupMessage.clear(); - return false; - } - // 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")) @@ -973,8 +666,7 @@ static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const R // for consistency with other backends (e.g. Chromium's) and other UA implementations like FF. It's done // in the backend here instead of in XHR code since in XHR CORS checking prevents us from this kind of // late header manipulation. - if ((request.httpMethod() == "POST" || request.httpMethod() == "PUT") - && (!request.httpBody() || request.httpBody()->isEmpty())) + if ((request.httpMethod() == "POST" || request.httpMethod() == "PUT") && !d->m_bodySize) soup_message_headers_set_content_length(soupMessage->request_headers, 0); g_signal_connect(d->m_soupMessage.get(), "notify::tls-errors", G_CALLBACK(tlsErrorsChangedCallback), handle); @@ -982,19 +674,14 @@ static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const R g_signal_connect(d->m_soupMessage.get(), "wrote-body-data", G_CALLBACK(wroteBodyDataCallback), handle); unsigned flags = SOUP_MESSAGE_NO_REDIRECT; -#if SOUP_CHECK_VERSION(2, 49, 91) - // Ignore the connection limits in synchronous loads to avoid freezing the networking process. - // See https://bugs.webkit.org/show_bug.cgi?id=141508. - if (loadingSynchronousRequest) - flags |= SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS; -#endif soup_message_set_flags(d->m_soupMessage.get(), static_cast<SoupMessageFlags>(soup_message_get_flags(d->m_soupMessage.get()) | flags)); #if ENABLE(WEB_TIMING) - d->m_response.setResourceLoadTiming(ResourceLoadTiming::create()); +#if SOUP_CHECK_VERSION(2, 49, 91) + g_signal_connect(d->m_soupMessage.get(), "starting", G_CALLBACK(startingCallback), handle); +#endif g_signal_connect(d->m_soupMessage.get(), "network-event", G_CALLBACK(networkEventCallback), handle); g_signal_connect(d->m_soupMessage.get(), "restarted", G_CALLBACK(restartedCallback), handle); - g_signal_connect(d->m_soupMessage.get(), "wrote-body", G_CALLBACK(wroteBodyCallback), handle); #endif #if SOUP_CHECK_VERSION(2, 43, 1) @@ -1004,7 +691,7 @@ static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const R return true; } -static bool createSoupRequestAndMessageForHandle(ResourceHandle* handle, const ResourceRequest& request, bool isHTTPFamilyRequest) +static bool createSoupRequestAndMessageForHandle(ResourceHandle* handle, const ResourceRequest& request) { ResourceHandleInternal* d = handle->getInternal(); @@ -1020,7 +707,7 @@ static bool createSoupRequestAndMessageForHandle(ResourceHandle* handle, const R } // SoupMessages are only applicable to HTTP-family requests. - if (isHTTPFamilyRequest && !createSoupMessageForHandleAndRequest(handle, request)) { + if (request.url().protocolIsInHTTPFamily() && !createSoupMessageForHandleAndRequest(handle, request)) { d->m_soupRequest.clear(); return false; } @@ -1044,15 +731,14 @@ bool ResourceHandle::start() // Only allow the POST and GET methods for non-HTTP requests. const ResourceRequest& request = firstRequest(); - bool isHTTPFamilyRequest = request.url().protocolIsInHTTPFamily(); - if (!isHTTPFamilyRequest && request.httpMethod() != "GET" && request.httpMethod() != "POST") { + if (!request.url().protocolIsInHTTPFamily() && request.httpMethod() != "GET" && request.httpMethod() != "POST") { this->scheduleFailure(InvalidURLFailure); // Error must not be reported immediately return true; } applyAuthenticationToRequest(this, firstRequest(), false); - if (!createSoupRequestAndMessageForHandle(this, request, isHTTPFamilyRequest)) { + if (!createSoupRequestAndMessageForHandle(this, request)) { this->scheduleFailure(InvalidURLFailure); // Error must not be reported immediately return true; } @@ -1064,19 +750,36 @@ bool ResourceHandle::start() return true; } +RefPtr<ResourceHandle> ResourceHandle::releaseForDownload(ResourceHandleClient* downloadClient) +{ + // We don't adopt the ref, as it will be released by cleanupSoupRequestOperation, which should always run. + ResourceHandle* newHandle = new ResourceHandle(d->m_context.get(), firstRequest(), nullptr, d->m_defersLoading, d->m_shouldContentSniff); + newHandle->relaxAdoptionRequirement(); + std::swap(d, newHandle->d); + + g_signal_handlers_disconnect_matched(newHandle->d->m_soupMessage.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); + g_object_set_data(G_OBJECT(newHandle->d->m_soupMessage.get()), "handle", newHandle); + + newHandle->d->m_client = downloadClient; + continueAfterDidReceiveResponse(newHandle); + + return newHandle; +} + +void ResourceHandle::timeoutFired() +{ + client()->didFail(this, ResourceError::timeoutError(firstRequest().url())); + cancel(); +} + void ResourceHandle::sendPendingRequest() { #if ENABLE(WEB_TIMING) - if (d->m_response.resourceLoadTiming()) - d->m_response.resourceLoadTiming()->requestTime = monotonicallyIncreasingTime(); + m_requestTime = monotonicallyIncreasingTime(); #endif - if (d->m_firstRequest.timeoutInterval() > 0) { - // soup_add_timeout returns a GSource* whose only reference is owned by - // the context. We need to have our own reference to it, hence not using adoptRef. - d->m_timeoutSource = soup_add_timeout(g_main_context_get_thread_default(), - d->m_firstRequest.timeoutInterval() * 1000, requestTimeoutCallback, this); - } + if (d->m_firstRequest.timeoutInterval() > 0) + d->m_timeoutSource.startOneShot(d->m_firstRequest.timeoutInterval()); // Balanced by a deref() in cleanupSoupRequestOperation, which should always run. ref(); @@ -1099,46 +802,21 @@ bool ResourceHandle::shouldUseCredentialStorage() return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily(); } -void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host) -{ - allowsAnyHTTPSCertificateHosts().add(host.lower()); -} - -void ResourceHandle::setClientCertificate(const String& host, GTlsCertificate* certificate) -{ - clientCertificates().add(host.lower(), HostTLSCertificateSet()).iterator->value.add(certificate); -} - -void ResourceHandle::setIgnoreSSLErrors(bool ignoreSSLErrors) -{ - gIgnoreSSLErrors = ignoreSSLErrors; -} - -#if PLATFORM(GTK) -void getCredentialFromPersistentStoreCallback(const Credential& credential, void* data) -{ - static_cast<ResourceHandle*>(data)->continueDidReceiveAuthenticationChallenge(credential); -} -#endif - void ResourceHandle::continueDidReceiveAuthenticationChallenge(const Credential& credentialFromPersistentStorage) { ASSERT(!d->m_currentWebChallenge.isNull()); AuthenticationChallenge& challenge = d->m_currentWebChallenge; - ASSERT(challenge.soupSession()); - ASSERT(challenge.soupMessage()); + ASSERT(d->m_soupMessage); if (!credentialFromPersistentStorage.isEmpty()) challenge.setProposedCredential(credentialFromPersistentStorage); if (!client()) { - soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage()); + soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get()); clearAuthentication(); return; } - ASSERT(challenge.soupSession()); - ASSERT(challenge.soupMessage()); client()->didReceiveAuthenticationChallenge(this, challenge); } @@ -1146,6 +824,8 @@ void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChall { ASSERT(d->m_currentWebChallenge.isNull()); + String partition = firstRequest().cachePartition(); + // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly. bool useCredentialStorage = shouldUseCredentialStorage(); if (useCredentialStorage) { @@ -1153,17 +833,17 @@ void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChall // 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()); + CredentialStorage::defaultCredentialStorage().remove(partition, challenge.protectionSpace()); } if (!challenge.previousFailureCount()) { - Credential credential = CredentialStorage::get(challenge.protectionSpace()); + Credential credential = CredentialStorage::defaultCredentialStorage().get(partition, challenge.protectionSpace()); if (!credential.isEmpty() && credential != d->m_initialCredential) { ASSERT(credential.persistence() == CredentialPersistenceNone); // Store the credential back, possibly adding it as a default for this directory. if (isAuthenticationFailureStatusCode(challenge.failureResponse().httpStatusCode())) - CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url()); + CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url()); soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data()); return; @@ -1172,18 +852,18 @@ void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChall } d->m_currentWebChallenge = challenge; - soup_session_pause_message(challenge.soupSession(), challenge.soupMessage()); + soup_session_pause_message(d->soupSession(), d->m_soupMessage.get()); -#if PLATFORM(GTK) // We could also do this before we even start the request, but that would be at the expense // of all request latency, versus a one-time latency for the small subset of requests that // use HTTP authentication. In the end, this doesn't matter much, because persistent credentials // will become session credentials after the first use. - if (useCredentialStorage) { - credentialBackingStore().credentialForChallenge(challenge, getCredentialFromPersistentStoreCallback, this); + if (useCredentialStorage && d->m_context && d->m_context->isValid()) { + d->m_context->storageSession().getCredentialFromPersistentStorage(challenge.protectionSpace(), [this, protectedThis = makeRef(*this)] (Credential&& credential) { + continueDidReceiveAuthenticationChallenge(WTFMove(credential)); + }); return; } -#endif continueDidReceiveAuthenticationChallenge(Credential()); } @@ -1193,7 +873,7 @@ void ResourceHandle::receivedRequestToContinueWithoutCredential(const Authentica ASSERT(!challenge.isNull()); if (challenge != d->m_currentWebChallenge) return; - soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage()); + soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get()); clearAuthentication(); } @@ -1210,26 +890,25 @@ void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge return; } + String partition = firstRequest().cachePartition(); + if (shouldUseCredentialStorage()) { // Eventually we will manage per-session credentials only internally or use some newly-exposed API from libsoup, // because once we authenticate via libsoup, there is no way to ignore it for a particular request. Right now, // we place the credentials in the store even though libsoup will never fire the authenticate signal again for // this protection space. if (credential.persistence() == CredentialPersistenceForSession || credential.persistence() == CredentialPersistencePermanent) - CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url()); + CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url()); -#if PLATFORM(GTK) if (credential.persistence() == CredentialPersistencePermanent) { d->m_credentialDataToSaveInPersistentStore.credential = credential; - d->m_credentialDataToSaveInPersistentStore.challenge = challenge; + d->m_credentialDataToSaveInPersistentStore.protectionSpace = challenge.protectionSpace(); } -#endif } - ASSERT(challenge.soupSession()); - ASSERT(challenge.soupMessage()); + ASSERT(d->m_soupMessage); soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data()); - soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage()); + soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get()); clearAuthentication(); } @@ -1245,9 +924,8 @@ void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challen return; } - ASSERT(challenge.soupSession()); - ASSERT(challenge.soupMessage()); - soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage()); + ASSERT(d->m_soupMessage); + soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get()); if (client()) client()->receivedCancellation(this, challenge); @@ -1255,6 +933,18 @@ void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challen clearAuthentication(); } +void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&) +{ + ASSERT_NOT_REACHED(); +} + +void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge& challenge) +{ + // This is only used by layout tests, soup based ports don't implement this. + notImplemented(); + receivedRequestToContinueWithoutCredential(challenge); +} + static bool waitingToSendRequest(ResourceHandle* handle) { // We need to check for d->m_soupRequest because the request may have raised a failure @@ -1270,10 +960,7 @@ void ResourceHandle::platformSetDefersLoading(bool defersLoading) // Except when canceling a possible timeout timer, we only need to take action here to UN-defer loading. if (defersLoading) { - if (d->m_timeoutSource) { - g_source_destroy(d->m_timeoutSource.get()); - d->m_timeoutSource.clear(); - } + d->m_timeoutSource.stop(); return; } @@ -1292,27 +979,9 @@ void ResourceHandle::platformSetDefersLoading(bool defersLoading) } } -bool ResourceHandle::loadsBlocked() +void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext*, const ResourceRequest&, StoredCredentials, ResourceError&, ResourceResponse&, Vector<char>&) { - return false; -} - -void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) -{ - ASSERT(!loadingSynchronousRequest); - if (loadingSynchronousRequest) // In practice this cannot happen, but if for some reason it does, - return; // we want to avoid accidentally going into an infinite loop of requests. - - WebCoreSynchronousLoader syncLoader(error, response, sessionFromContext(context), data, storedCredentials); - RefPtr<ResourceHandle> handle = create(context, request, &syncLoader, false /*defersLoading*/, false /*shouldContentSniff*/); - if (!handle) - return; - - // If the request has already failed, do not run the main loop, or else we'll block indefinitely. - if (handle->d->m_scheduledFailureType != NoFailure) - return; - - syncLoader.run(); + ASSERT_NOT_REACHED(); } static void readCallback(GObject*, GAsyncResult* asyncResult, gpointer data) @@ -1378,34 +1047,18 @@ static void readCallback(GObject*, GAsyncResult* asyncResult, gpointer data) d->m_cancellable.get(), readCallback, handle.get()); } -void ResourceHandle::continueWillSendRequest(const ResourceRequest& request) +void ResourceHandle::continueWillSendRequest(ResourceRequest&& request) { - ASSERT(client()); - ASSERT(client()->usesAsyncCallbacks()); - continueAfterWillSendRequest(this, request); + ASSERT(!client() || client()->usesAsyncCallbacks()); + continueAfterWillSendRequest(this, WTFMove(request)); } void ResourceHandle::continueDidReceiveResponse() { - ASSERT(client()); - ASSERT(client()->usesAsyncCallbacks()); + ASSERT(!client() || client()->usesAsyncCallbacks()); continueAfterDidReceiveResponse(this); } -void ResourceHandle::continueShouldUseCredentialStorage(bool) -{ - ASSERT(client()); - ASSERT(client()->usesAsyncCallbacks()); - // FIXME: Implement this method if needed: https://bugs.webkit.org/show_bug.cgi?id=126114. } -static gboolean requestTimeoutCallback(gpointer data) -{ - RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); - handle->client()->didFail(handle.get(), ResourceError::timeoutError(handle->getInternal()->m_firstRequest.url().string())); - handle->cancel(); - - return FALSE; -} - -} +#endif diff --git a/Source/WebCore/platform/network/soup/ResourceRequest.h b/Source/WebCore/platform/network/soup/ResourceRequest.h index 45b1ce880..04d5d4841 100644 --- a/Source/WebCore/platform/network/soup/ResourceRequest.h +++ b/Source/WebCore/platform/network/soup/ResourceRequest.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006 Apple Inc. All rights reserved. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -86,7 +86,7 @@ namespace WebCore { updateFromSoupRequest(soupRequest); } - void updateFromDelegatePreservingOldHTTPBody(const ResourceRequest& delegateProvidedRequest) { *this = delegateProvidedRequest; } + void updateFromDelegatePreservingOldProperties(const ResourceRequest& delegateProvidedRequest) { *this = delegateProvidedRequest; } bool acceptEncoding() const { return m_acceptEncoding; } void setAcceptEncoding(bool acceptEncoding) { m_acceptEncoding = acceptEncoding; } @@ -94,7 +94,6 @@ namespace WebCore { void updateSoupMessageHeaders(SoupMessageHeaders*) const; void updateFromSoupMessageHeaders(SoupMessageHeaders*); void updateSoupMessage(SoupMessage*) const; - SoupMessage* toSoupMessage() const; void updateFromSoupMessage(SoupMessage*); void updateSoupRequest(SoupRequest*) const; void updateFromSoupRequest(SoupRequest*); @@ -107,6 +106,9 @@ namespace WebCore { GUniquePtr<SoupURI> createSoupURI() const; + template<class Encoder> void encodeWithPlatformData(Encoder&) const; + template<class Decoder> bool decodeWithPlatformData(Decoder&); + private: friend class ResourceRequestBase; @@ -115,33 +117,74 @@ namespace WebCore { uint64_t m_initiatingPageID; void updateSoupMessageMembers(SoupMessage*) const; + void updateSoupMessageBody(SoupMessage*) const; void doUpdatePlatformRequest() { } void doUpdateResourceRequest() { } void doUpdatePlatformHTTPBody() { } void doUpdateResourceHTTPBody() { } - PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } - void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } + void doPlatformSetAsIsolatedCopy(const ResourceRequest&) { } }; - struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { - }; +template<class Encoder> +void ResourceRequest::encodeWithPlatformData(Encoder& encoder) const +{ + encodeBase(encoder); + + // FIXME: Do not encode HTTP message body. + // 1. It can be large and thus costly to send across. + // 2. It is misleading to provide a body with some requests, while others use body streams, which cannot be serialized at all. + encoder << static_cast<bool>(m_httpBody); + if (m_httpBody) + encoder << m_httpBody->flattenToString(); + + encoder << static_cast<uint32_t>(m_soupFlags); + encoder << m_initiatingPageID; +} + +template<class Decoder> +bool ResourceRequest::decodeWithPlatformData(Decoder& decoder) +{ + if (!decodeBase(decoder)) + return false; + + bool hasHTTPBody; + if (!decoder.decode(hasHTTPBody)) + return false; + if (hasHTTPBody) { + String httpBody; + if (!decoder.decode(httpBody)) + return false; + setHTTPBody(FormData::create(httpBody.utf8())); + } + + uint32_t soupMessageFlags; + if (!decoder.decode(soupMessageFlags)) + return false; + m_soupFlags = static_cast<SoupMessageFlags>(soupMessageFlags); + + uint64_t initiatingPageID; + if (!decoder.decode(initiatingPageID)) + return false; + m_initiatingPageID = initiatingPageID; + + return true; +} + #if SOUP_CHECK_VERSION(2, 43, 1) inline SoupMessagePriority toSoupMessagePriority(ResourceLoadPriority priority) { switch (priority) { - case ResourceLoadPriorityUnresolved: - return SOUP_MESSAGE_PRIORITY_NORMAL; - case ResourceLoadPriorityVeryLow: + case ResourceLoadPriority::VeryLow: return SOUP_MESSAGE_PRIORITY_VERY_LOW; - case ResourceLoadPriorityLow: + case ResourceLoadPriority::Low: return SOUP_MESSAGE_PRIORITY_LOW; - case ResourceLoadPriorityMedium: + case ResourceLoadPriority::Medium: return SOUP_MESSAGE_PRIORITY_NORMAL; - case ResourceLoadPriorityHigh: + case ResourceLoadPriority::High: return SOUP_MESSAGE_PRIORITY_HIGH; - case ResourceLoadPriorityVeryHigh: + case ResourceLoadPriority::VeryHigh: return SOUP_MESSAGE_PRIORITY_VERY_HIGH; } diff --git a/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp b/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp index cadb84b29..64578615c 100644 --- a/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp @@ -18,16 +18,82 @@ */ #include "config.h" + +#if USE(SOUP) #include "ResourceRequest.h" +#include "BlobData.h" +#include "BlobRegistryImpl.h" #include "GUniquePtrSoup.h" #include "HTTPParsers.h" #include "MIMETypeRegistry.h" +#include "SharedBuffer.h" +#include "WebKitSoupRequestGeneric.h" #include <wtf/text/CString.h> #include <wtf/text/WTFString.h> namespace WebCore { +static uint64_t appendEncodedBlobItemToSoupMessageBody(SoupMessage* soupMessage, const BlobDataItem& blobItem) +{ + switch (blobItem.type()) { + case BlobDataItem::Type::Data: + soup_message_body_append(soupMessage->request_body, SOUP_MEMORY_TEMPORARY, blobItem.data().data()->data() + blobItem.offset(), blobItem.length()); + return blobItem.length(); + case BlobDataItem::Type::File: { + if (!isValidFileTime(blobItem.file()->expectedModificationTime())) + return 0; + + time_t fileModificationTime; + if (!getFileModificationTime(blobItem.file()->path(), fileModificationTime) + || fileModificationTime != static_cast<time_t>(blobItem.file()->expectedModificationTime())) + return 0; + + if (RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(blobItem.file()->path())) { + GUniquePtr<SoupBuffer> soupBuffer(buffer->createSoupBuffer(blobItem.offset(), blobItem.length() == BlobDataItem::toEndOfFile ? 0 : blobItem.length())); + soup_message_body_append_buffer(soupMessage->request_body, soupBuffer.get()); + return soupBuffer->length; + } + break; + } + } + + return 0; +} + +void ResourceRequest::updateSoupMessageBody(SoupMessage* soupMessage) const +{ + auto* formData = httpBody(); + if (!formData || formData->isEmpty()) + return; + + soup_message_body_set_accumulate(soupMessage->request_body, FALSE); + uint64_t bodySize = 0; + for (const auto& element : formData->elements()) { + switch (element.m_type) { + case FormDataElement::Type::Data: + bodySize += element.m_data.size(); + soup_message_body_append(soupMessage->request_body, SOUP_MEMORY_TEMPORARY, element.m_data.data(), element.m_data.size()); + break; + case FormDataElement::Type::EncodedFile: + if (RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(element.m_filename)) { + GUniquePtr<SoupBuffer> soupBuffer(buffer->createSoupBuffer()); + bodySize += buffer->size(); + soup_message_body_append_buffer(soupMessage->request_body, soupBuffer.get()); + } + break; + case FormDataElement::Type::EncodedBlob: + if (auto* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(element.m_url)) { + for (const auto& item : blobData->items()) + bodySize += appendEncodedBlobItemToSoupMessageBody(soupMessage, item); + } + break; + } + } + + ASSERT(bodySize == static_cast<uint64_t>(soupMessage->request_body->length)); +} + void ResourceRequest::updateSoupMessageMembers(SoupMessage* soupMessage) const { updateSoupMessageHeaders(soupMessage->request_headers); @@ -50,7 +116,7 @@ void ResourceRequest::updateSoupMessageHeaders(SoupMessageHeaders* soupHeaders) 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->key.string().utf8().data(), it->value.utf8().data()); + soup_message_headers_append(soupHeaders, it->key.utf8().data(), it->value.utf8().data()); } } @@ -73,25 +139,12 @@ void ResourceRequest::updateSoupMessage(SoupMessage* soupMessage) const soup_message_set_uri(soupMessage, uri.get()); updateSoupMessageMembers(soupMessage); -} - -SoupMessage* ResourceRequest::toSoupMessage() const -{ - SoupMessage* soupMessage = soup_message_new(httpMethod().ascii().data(), url().string().utf8().data()); - if (!soupMessage) - return 0; - - updateSoupMessageMembers(soupMessage); - - // 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; + updateSoupMessageBody(soupMessage); } void ResourceRequest::updateFromSoupMessage(SoupMessage* soupMessage) { - bool shouldPortBeResetToZero = m_url.hasPort() && !m_url.port(); + bool shouldPortBeResetToZero = m_url.port() && !m_url.port().value(); m_url = URL(soup_message_get_uri(soupMessage)); // SoupURI cannot differeniate between an explicitly specified port 0 and @@ -119,12 +172,14 @@ static const char* gSoupRequestInitiatingPageIDKey = "wk-soup-request-initiating void ResourceRequest::updateSoupRequest(SoupRequest* soupRequest) const { - if (!m_initiatingPageID) - return; + if (m_initiatingPageID) { + uint64_t* initiatingPageIDPtr = static_cast<uint64_t*>(fastMalloc(sizeof(uint64_t))); + *initiatingPageIDPtr = m_initiatingPageID; + g_object_set_data_full(G_OBJECT(soupRequest), g_intern_static_string(gSoupRequestInitiatingPageIDKey), initiatingPageIDPtr, fastFree); + } - uint64_t* initiatingPageIDPtr = static_cast<uint64_t*>(fastMalloc(sizeof(uint64_t))); - *initiatingPageIDPtr = m_initiatingPageID; - g_object_set_data_full(G_OBJECT(soupRequest), g_intern_static_string(gSoupRequestInitiatingPageIDKey), initiatingPageIDPtr, fastFree); + if (WEBKIT_IS_SOUP_REQUEST_GENERIC(soupRequest)) + webkitSoupRequestGenericSetRequest(WEBKIT_SOUP_REQUEST_GENERIC(soupRequest), *this); } void ResourceRequest::updateFromSoupRequest(SoupRequest* soupRequest) @@ -153,13 +208,7 @@ GUniquePtr<SoupURI> ResourceRequest::createSoupURI() const return GUniquePtr<SoupURI>(soup_uri_new(urlString.utf8().data())); } - GUniquePtr<SoupURI> soupURI; - if (m_url.hasFragmentIdentifier()) { - URL url = m_url; - url.removeFragmentIdentifier(); - soupURI.reset(soup_uri_new(url.string().utf8().data())); - } else - soupURI = m_url.createSoupURI(); + GUniquePtr<SoupURI> soupURI = m_url.createSoupURI(); // Versions of libsoup prior to 2.42 have a soup_uri_new that will convert empty passwords that are not // prefixed by a colon into null. Some parts of soup like the SoupAuthenticationManager will only be active @@ -176,3 +225,5 @@ GUniquePtr<SoupURI> ResourceRequest::createSoupURI() const } } + +#endif diff --git a/Source/WebCore/platform/network/soup/ResourceResponse.h b/Source/WebCore/platform/network/soup/ResourceResponse.h index 4891f935f..9e206bba9 100644 --- a/Source/WebCore/platform/network/soup/ResourceResponse.h +++ b/Source/WebCore/platform/network/soup/ResourceResponse.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -23,13 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceResponse_h -#define ResourceResponse_h +#pragma once #include "ResourceResponseBase.h" #include <libsoup/soup.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> namespace WebCore { @@ -42,8 +41,8 @@ public: { } - ResourceResponse(const URL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) - : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename) + ResourceResponse(const URL& url, const String& mimeType, long long expectedLength, const String& textEncodingName) + : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName) , m_soupFlags(static_cast<SoupMessageFlags>(0)) , m_tlsErrors(static_cast<GTlsCertificateFlags>(0)) { @@ -57,7 +56,7 @@ public: updateFromSoupMessage(soupMessage); } - SoupMessage* toSoupMessage() const; + void updateSoupMessageHeaders(SoupMessageHeaders*) const; void updateFromSoupMessage(SoupMessage*); void updateFromSoupMessageHeaders(const SoupMessageHeaders*); @@ -73,7 +72,8 @@ public: GTlsCertificateFlags soupMessageTLSErrors() const { return m_tlsErrors; } void setSoupMessageTLSErrors(GTlsCertificateFlags tlsErrors) { m_tlsErrors = tlsErrors; } - bool platformResponseIsUpToDate() const { return false; } + template<class Encoder> void encode(Encoder&) const; + template<class Decoder> static bool decode(Decoder&, ResourceResponse&); private: friend class ResourceResponseBase; @@ -84,14 +84,25 @@ private: GTlsCertificateFlags m_tlsErrors; void doUpdateResourceResponse() { } - - PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; } - void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { } + String platformSuggestedFilename() const; + CertificateInfo platformCertificateInfo() const; }; -struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { -}; +template<class Encoder> +void ResourceResponse::encode(Encoder& encoder) const +{ + ResourceResponseBase::encode(encoder); + encoder.encodeEnum(m_soupFlags); +} + +template<class Decoder> +bool ResourceResponse::decode(Decoder& decoder, ResourceResponse& response) +{ + if (!ResourceResponseBase::decode(decoder, response)) + return false; + if (!decoder.decodeEnum(response.m_soupFlags)) + return false; + return true; +} } // namespace WebCore - -#endif // ResourceResponse_h diff --git a/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp b/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp index a7a8fa435..50cfa3e6e 100644 --- a/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp @@ -19,45 +19,37 @@ */ #include "config.h" + +#if USE(SOUP) + #include "ResourceResponse.h" +#include "HTTPHeaderNames.h" #include "HTTPParsers.h" #include "MIMETypeRegistry.h" #include <wtf/text/CString.h> -#include <wtf/text/StringBuilder.h> #include <wtf/text/WTFString.h> namespace WebCore { -SoupMessage* ResourceResponse::toSoupMessage() const +void ResourceResponse::updateSoupMessageHeaders(SoupMessageHeaders* soupHeaders) 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->key.string().utf8().data(), it->value.utf8().data()); - } - - soup_message_set_flags(soupMessage, m_soupFlags); - - g_object_set(G_OBJECT(soupMessage), "tls-certificate", m_certificate.get(), "tls-errors", m_tlsErrors, NULL); - - // Body data is not in the message. - return soupMessage; + for (const auto& header : httpHeaderFields()) + soup_message_headers_append(soupHeaders, header.key.utf8().data(), header.value.utf8().data()); } void ResourceResponse::updateFromSoupMessage(SoupMessage* soupMessage) { m_url = URL(soup_message_get_uri(soupMessage)); + switch (soup_message_get_http_version(soupMessage)) { + case SOUP_HTTP_1_0: + m_httpVersion = AtomicString("HTTP/1.0", AtomicString::ConstructFromLiteral); + break; + case SOUP_HTTP_1_1: + m_httpVersion = AtomicString("HTTP/1.1", AtomicString::ConstructFromLiteral); + break; + } m_httpStatusCode = soupMessage->status_code; setHTTPStatusText(soupMessage->reason_phrase); @@ -95,6 +87,19 @@ void ResourceResponse::updateFromSoupMessageHeaders(const SoupMessageHeaders* me setTextEncodingName(extractCharsetFromMediaType(contentType)); setExpectedContentLength(soup_message_headers_get_content_length(headers)); - setSuggestedFilename(filenameFromHTTPContentDisposition(httpHeaderField("Content-Disposition")));} +} +CertificateInfo ResourceResponse::platformCertificateInfo() const +{ + return CertificateInfo(m_certificate.get(), m_tlsErrors); +} + +String ResourceResponse::platformSuggestedFilename() const +{ + String contentDisposition(httpHeaderField(HTTPHeaderName::ContentDisposition)); + return filenameFromHTTPContentDisposition(String::fromUTF8WithLatin1Fallback(contentDisposition.characters8(), contentDisposition.length())); } + +} + +#endif diff --git a/Source/WebCore/platform/network/soup/SocketStreamError.h b/Source/WebCore/platform/network/soup/SocketStreamError.h deleted file mode 100644 index 0676893ce..000000000 --- a/Source/WebCore/platform/network/soup/SocketStreamError.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2009, 2011 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() { } - SocketStreamError(int errorCode, const gchar* description) - : SocketStreamErrorBase(errorCode, String(), String(description)) - { - } - - }; - -} // namespace WebCore - -#endif // SocketStreamError_h diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandle.h b/Source/WebCore/platform/network/soup/SocketStreamHandle.h deleted file mode 100644 index fa0168805..000000000 --- a/Source/WebCore/platform/network/soup/SocketStreamHandle.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2009 Google Inc. All rights reserved. - * Copyright (C) 2012 Samsung Electronics 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: - * - * * 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> -#include <wtf/gobject/GRefPtr.h> - -namespace WebCore { - - class AuthenticationChallenge; - class Credential; - class SocketStreamHandleClient; - - class SocketStreamHandle : public RefCounted<SocketStreamHandle>, public SocketStreamHandleBase { - public: - static PassRefPtr<SocketStreamHandle> create(const URL& url, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(url, client)); } - static PassRefPtr<SocketStreamHandle> create(GSocketConnection* socketConnection, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(socketConnection, 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<GPollableOutputStream> m_outputStream; - GRefPtr<GSource> m_writeReadySource; - std::unique_ptr<char[]> m_readBuffer; - void* m_id; - - SocketStreamHandle(const URL&, SocketStreamHandleClient*); - SocketStreamHandle(GSocketConnection*, 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/SocketStreamErrorBase.h b/Source/WebCore/platform/network/soup/SocketStreamHandleImpl.h index 132063f2b..0459e907e 100644 --- a/Source/WebCore/platform/network/SocketStreamErrorBase.h +++ b/Source/WebCore/platform/network/soup/SocketStreamHandleImpl.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2009 Apple Inc. All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2012 Samsung Electronics Ltd. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -29,58 +30,54 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SocketStreamErrorBase_h -#define SocketStreamErrorBase_h +#pragma once -#include <wtf/text/WTFString.h> +#include "SocketStreamHandle.h" -namespace WebCore { +#if USE(SOUP) + +#include "SessionID.h" +#include <wtf/RefCounted.h> +#include <wtf/glib/GRefPtr.h> - class SocketStreamError; +namespace WebCore { - class SocketStreamErrorBase { - public: - // Makes a deep copy. Useful for when you need to use a SocketStreamError on another thread. - SocketStreamError copy() const; +class SocketStreamError; +class SocketStreamHandleClient; - bool isNull() const { return m_isNull; } +class SocketStreamHandleImpl final : public SocketStreamHandle { +public: + static Ref<SocketStreamHandleImpl> create(const URL&, SocketStreamHandleClient&, SessionID, const String&); + static Ref<SocketStreamHandle> create(GSocketConnection*, SocketStreamHandleClient&); - int errorCode() const { return m_errorCode; } - const String& failingURL() const { return m_failingURL; } - const String& localizedDescription() const { return m_localizedDescription; } + virtual ~SocketStreamHandleImpl(); - static bool compare(const SocketStreamError&, const SocketStreamError&); +private: + SocketStreamHandleImpl(const URL&, SocketStreamHandleClient&); - protected: - SocketStreamErrorBase() - : m_errorCode(0) - , m_isNull(true) - { - } + std::optional<size_t> platformSend(const char* data, size_t length) final; + void platformClose() final; - explicit SocketStreamErrorBase(int errorCode) - : m_errorCode(errorCode) - , m_isNull(false) - { - } + void beginWaitingForSocketWritability(); + void stopWaitingForSocketWritability(); - SocketStreamErrorBase(int errorCode, const String& failingURL, const String& localizedDescription) - : m_errorCode(errorCode) - , m_failingURL(failingURL) - , m_localizedDescription(localizedDescription) - , m_isNull(false) - { - } + static void connectedCallback(GSocketClient*, GAsyncResult*, SocketStreamHandleImpl*); + static void readReadyCallback(GInputStream*, GAsyncResult*, SocketStreamHandleImpl*); + static gboolean writeReadyCallback(GPollableOutputStream*, SocketStreamHandleImpl*); - int m_errorCode; - String m_failingURL; - String m_localizedDescription; - bool m_isNull; - }; + void connected(GRefPtr<GSocketConnection>&&); + void readBytes(gssize); + void didFail(SocketStreamError&&); + void writeReady(); - 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); } + GRefPtr<GSocketConnection> m_socketConnection; + GRefPtr<GInputStream> m_inputStream; + GRefPtr<GPollableOutputStream> m_outputStream; + GRefPtr<GSource> m_writeReadySource; + GRefPtr<GCancellable> m_cancellable; + std::unique_ptr<char[]> m_readBuffer; +}; -} // namespace WebCore +} // namespace WebCore -#endif // SocketStreamErrorBase_h +#endif diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandleImplSoup.cpp b/Source/WebCore/platform/network/soup/SocketStreamHandleImplSoup.cpp new file mode 100644 index 000000000..20d19e3b1 --- /dev/null +++ b/Source/WebCore/platform/network/soup/SocketStreamHandleImplSoup.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2009, 2011 Google Inc. All rights reserved. + * Copyright (C) 2012 Samsung Electronics 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: + * + * * 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 "SocketStreamHandleImpl.h" + +#if USE(SOUP) + +#include "Logging.h" +#include "Settings.h" +#include "SocketStreamError.h" +#include "SocketStreamHandleClient.h" +#include "URL.h" +#include <gio/gio.h> +#include <glib.h> +#include <wtf/Vector.h> +#include <wtf/glib/GUniquePtr.h> +#include <wtf/text/CString.h> + +#define READ_BUFFER_SIZE 1024 + +namespace WebCore { + +static gboolean wssConnectionAcceptCertificateCallback(GTlsConnection*, GTlsCertificate*, GTlsCertificateFlags) +{ + return TRUE; +} + +static void wssSocketClientEventCallback(GSocketClient*, GSocketClientEvent event, GSocketConnectable*, GIOStream* connection) +{ + if (event != G_SOCKET_CLIENT_TLS_HANDSHAKING) + return; + + g_signal_connect(connection, "accept-certificate", G_CALLBACK(wssConnectionAcceptCertificateCallback), nullptr); +} + +Ref<SocketStreamHandleImpl> SocketStreamHandleImpl::create(const URL& url, SocketStreamHandleClient& client, SessionID, const String&) +{ + Ref<SocketStreamHandleImpl> socket = adoptRef(*new SocketStreamHandleImpl(url, client)); + + unsigned port = url.port() ? url.port().value() : (url.protocolIs("wss") ? 443 : 80); + GRefPtr<GSocketClient> socketClient = adoptGRef(g_socket_client_new()); + if (url.protocolIs("wss")) { + g_socket_client_set_tls(socketClient.get(), TRUE); + // FIXME: this is only used by tests, but using Settings from here is a layering violation. + if (Settings::allowsAnySSLCertificate()) + g_signal_connect(socketClient.get(), "event", G_CALLBACK(wssSocketClientEventCallback), nullptr); + } + Ref<SocketStreamHandle> protectedSocketStreamHandle = socket.copyRef(); + g_socket_client_connect_to_host_async(socketClient.get(), url.host().utf8().data(), port, socket->m_cancellable.get(), + reinterpret_cast<GAsyncReadyCallback>(connectedCallback), &protectedSocketStreamHandle.leakRef()); + return socket; +} + +Ref<SocketStreamHandle> SocketStreamHandleImpl::create(GSocketConnection* socketConnection, SocketStreamHandleClient& client) +{ + Ref<SocketStreamHandleImpl> socket = adoptRef(*new SocketStreamHandleImpl(URL(), client)); + + GRefPtr<GSocketConnection> connection = socketConnection; + socket->connected(WTFMove(connection)); + return WTFMove(socket); +} + +SocketStreamHandleImpl::SocketStreamHandleImpl(const URL& url, SocketStreamHandleClient& client) + : SocketStreamHandle(url, client) + , m_cancellable(adoptGRef(g_cancellable_new())) +{ + LOG(Network, "SocketStreamHandle %p new client %p", this, &m_client); +} + +SocketStreamHandleImpl::~SocketStreamHandleImpl() +{ + LOG(Network, "SocketStreamHandle %p delete", this); +} + +void SocketStreamHandleImpl::connected(GRefPtr<GSocketConnection>&& socketConnection) +{ + m_socketConnection = WTFMove(socketConnection); + m_outputStream = G_POLLABLE_OUTPUT_STREAM(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 = std::make_unique<char[]>(READ_BUFFER_SIZE); + + RefPtr<SocketStreamHandleImpl> protectedThis(this); + g_input_stream_read_async(m_inputStream.get(), m_readBuffer.get(), READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, m_cancellable.get(), + reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), protectedThis.leakRef()); + + m_state = Open; + m_client.didOpenSocketStream(*this); +} + +void SocketStreamHandleImpl::connectedCallback(GSocketClient* client, GAsyncResult* result, SocketStreamHandleImpl* handle) +{ + RefPtr<SocketStreamHandle> protectedThis = adoptRef(handle); + + // Always finish the connection, even if this SocketStreamHandle was cancelled earlier. + GUniqueOutPtr<GError> error; + GRefPtr<GSocketConnection> socketConnection = adoptGRef(g_socket_client_connect_to_host_finish(client, result, &error.outPtr())); + + // The SocketStreamHandle has been cancelled, so just close the connection, ignoring errors. + if (g_cancellable_is_cancelled(handle->m_cancellable.get())) { + if (socketConnection) + g_io_stream_close(G_IO_STREAM(socketConnection.get()), nullptr, nullptr); + return; + } + + if (error) + handle->didFail(SocketStreamError(error->code, String(), error->message)); + else + handle->connected(WTFMove(socketConnection)); +} + +void SocketStreamHandleImpl::readBytes(gssize bytesRead) +{ + if (!bytesRead) { + close(); + return; + } + + // The client can close the handle, potentially removing the last reference. + RefPtr<SocketStreamHandle> protectedThis(this); + std::optional<size_t> optionalLength; + if (bytesRead != -1) + optionalLength = static_cast<size_t>(bytesRead); + m_client.didReceiveSocketStreamData(*this, m_readBuffer.get(), optionalLength); + if (m_inputStream) { + g_input_stream_read_async(m_inputStream.get(), m_readBuffer.get(), READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, m_cancellable.get(), + reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), protectedThis.leakRef()); + } +} + +void SocketStreamHandleImpl::readReadyCallback(GInputStream* stream, GAsyncResult* result, SocketStreamHandleImpl* handle) +{ + RefPtr<SocketStreamHandle> protectedThis = adoptRef(handle); + + // Always finish the read, even if this SocketStreamHandle was cancelled earlier. + GUniqueOutPtr<GError> error; + gssize bytesRead = g_input_stream_read_finish(stream, result, &error.outPtr()); + + if (g_cancellable_is_cancelled(handle->m_cancellable.get())) + return; + + if (error) + handle->didFail(SocketStreamError(error->code, String(), error->message)); + else + handle->readBytes(bytesRead); +} + +void SocketStreamHandleImpl::didFail(SocketStreamError&& error) +{ + m_client.didFailSocketStream(*this, WTFMove(error)); +} + +void SocketStreamHandleImpl::writeReady() +{ + // We no longer have buffered data, so stop waiting for the socket to be writable. + if (!bufferedAmount()) { + stopWaitingForSocketWritability(); + return; + } + + sendPendingData(); +} + +std::optional<size_t> SocketStreamHandleImpl::platformSend(const char* data, size_t length) +{ + LOG(Network, "SocketStreamHandle %p platformSend", this); + if (!m_outputStream || !data) + return 0; + + GUniqueOutPtr<GError> error; + gssize written = g_pollable_output_stream_write_nonblocking(m_outputStream.get(), data, length, m_cancellable.get(), &error.outPtr()); + if (error) { + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) + beginWaitingForSocketWritability(); + else + didFail(SocketStreamError(error->code, String(), error->message)); + return std::nullopt; + } + + // If we did not send all the bytes we were given, we know that + // SocketStreamHandle will need to send more in the future. + if (written == -1 || static_cast<size_t>(written) < length) + beginWaitingForSocketWritability(); + + if (written == -1) + return std::nullopt; + + return static_cast<size_t>(written); +} + +void SocketStreamHandleImpl::platformClose() +{ + LOG(Network, "SocketStreamHandle %p platformClose", this); + // We cancel this handle first to disable all callbacks. + g_cancellable_cancel(m_cancellable.get()); + stopWaitingForSocketWritability(); + + if (m_socketConnection) { + GUniqueOutPtr<GError> error; + g_io_stream_close(G_IO_STREAM(m_socketConnection.get()), nullptr, &error.outPtr()); + if (error) + didFail(SocketStreamError(error->code, String(), error->message)); + m_socketConnection = nullptr; + } + + m_outputStream = nullptr; + m_inputStream = nullptr; + m_readBuffer = nullptr; + + m_client.didCloseSocketStream(*this); +} + +void SocketStreamHandleImpl::beginWaitingForSocketWritability() +{ + if (m_writeReadySource) // Already waiting. + return; + + m_writeReadySource = adoptGRef(g_pollable_output_stream_create_source(m_outputStream.get(), m_cancellable.get())); + ref(); + g_source_set_callback(m_writeReadySource.get(), reinterpret_cast<GSourceFunc>(writeReadyCallback), this, [](gpointer handle) { + static_cast<SocketStreamHandleImpl*>(handle)->deref(); + }); + g_source_attach(m_writeReadySource.get(), g_main_context_get_thread_default()); +} + +void SocketStreamHandleImpl::stopWaitingForSocketWritability() +{ + if (!m_writeReadySource) // Not waiting. + return; + + g_source_destroy(m_writeReadySource.get()); + m_writeReadySource = nullptr; +} + +gboolean SocketStreamHandleImpl::writeReadyCallback(GPollableOutputStream*, SocketStreamHandleImpl* handle) +{ + if (g_cancellable_is_cancelled(handle->m_cancellable.get())) + return G_SOURCE_REMOVE; + + handle->writeReady(); + return G_SOURCE_CONTINUE; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp b/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp deleted file mode 100644 index 71c21216d..000000000 --- a/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2009, 2011 Google Inc. All rights reserved. - * Copyright (C) 2012 Samsung Electronics 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: - * - * * 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 "URL.h" -#include "Logging.h" -#include "NotImplemented.h" -#include "SocketStreamError.h" -#include "SocketStreamHandleClient.h" - -#include <gio/gio.h> -#include <glib.h> - -#include <wtf/Vector.h> -#include <wtf/gobject/GUniquePtr.h> -#include <wtf/text/CString.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(GPollableOutputStream*, 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; -COMPILE_ASSERT(HashTraits<SocketStreamHandle*>::emptyValueIsZero, emptyMapValue_is_0); - -static SocketStreamHandle* getHandleFromId(void* id) -{ - 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 URL& url, SocketStreamHandleClient* client) - : SocketStreamHandleBase(url, client) -{ - LOG(Network, "SocketStreamHandle %p new client %p", this, m_client); - unsigned int port = url.hasPort() ? url.port() : (url.protocolIs("wss") ? 443 : 80); - - m_id = activateHandle(this); - GRefPtr<GSocketClient> socketClient = adoptGRef(g_socket_client_new()); - if (url.protocolIs("wss")) - g_socket_client_set_tls(socketClient.get(), TRUE); - g_socket_client_connect_to_host_async(socketClient.get(), url.host().utf8().data(), port, 0, - reinterpret_cast<GAsyncReadyCallback>(connectedCallback), m_id); -} - -SocketStreamHandle::SocketStreamHandle(GSocketConnection* socketConnection, SocketStreamHandleClient* client) - : SocketStreamHandleBase(URL(), client) -{ - LOG(Network, "SocketStreamHandle %p new client %p", this, m_client); - m_id = activateHandle(this); - connected(socketConnection, 0); -} - -SocketStreamHandle::~SocketStreamHandle() -{ - LOG(Network, "SocketStreamHandle %p delete", this); - // 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->didFailSocketStream(this, SocketStreamError(error->code, error->message)); - return; - } - - m_socketConnection = socketConnection; - m_outputStream = G_POLLABLE_OUTPUT_STREAM(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 = std::make_unique<char[]>(READ_BUFFER_SIZE); - g_input_stream_read_async(m_inputStream.get(), m_readBuffer.get(), READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, 0, - reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), m_id); - - m_state = Open; - m_client->didOpenSocketStream(this); -} - -void SocketStreamHandle::readBytes(signed long bytesRead, GError* error) -{ - if (error) { - m_client->didFailSocketStream(this, SocketStreamError(error->code, error->message)); - return; - } - - if (!bytesRead) { - close(); - return; - } - - // The client can close the handle, potentially removing the last reference. - RefPtr<SocketStreamHandle> protect(this); - m_client->didReceiveSocketStreamData(this, m_readBuffer.get(), bytesRead); - if (m_inputStream) // The client may have closed the connection. - g_input_stream_read_async(m_inputStream.get(), m_readBuffer.get(), 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) -{ - LOG(Network, "SocketStreamHandle %p platformSend", this); - if (!m_outputStream || !data) - return 0; - - GUniqueOutPtr<GError> error; - gssize written = g_pollable_output_stream_write_nonblocking(m_outputStream.get(), data, length, 0, &error.outPtr()); - if (error) { - if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) - beginWaitingForSocketWritability(); - else - m_client->didFailSocketStream(this, SocketStreamError(error->code, error->message)); - 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() -{ - LOG(Network, "SocketStreamHandle %p platformClose", this); - // We remove this handle from the active handles list first, to disable all callbacks. - deactivateHandle(this); - stopWaitingForSocketWritability(); - - if (m_socketConnection) { - GUniqueOutPtr<GError> error; - g_io_stream_close(G_IO_STREAM(m_socketConnection.get()), 0, &error.outPtr()); - if (error) - m_client->didFailSocketStream(this, SocketStreamError(error->code, error->message)); - m_socketConnection = 0; - } - - m_outputStream = 0; - m_inputStream = 0; - m_readBuffer = nullptr; - - m_client->didCloseSocketStream(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_pollable_output_stream_create_source(m_outputStream.get(), 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. - GUniqueOutPtr<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) { - if (socketConnection) - g_io_stream_close(G_IO_STREAM(socketConnection), 0, 0); - 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. - GUniqueOutPtr<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(GPollableOutputStream*, void* id) -{ - SocketStreamHandle* handle = getHandleFromId(id); - if (!handle) - return FALSE; - - handle->writeReady(); - return TRUE; -} - -} // namespace WebCore diff --git a/Source/WebCore/platform/network/soup/SoupNetworkProxySettings.h b/Source/WebCore/platform/network/soup/SoupNetworkProxySettings.h new file mode 100644 index 000000000..273267350 --- /dev/null +++ b/Source/WebCore/platform/network/soup/SoupNetworkProxySettings.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 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 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. + */ + +#pragma once + +#include <wtf/HashMap.h> +#include <wtf/glib/GUniquePtr.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +struct SoupNetworkProxySettings { + enum class Mode { Default, NoProxy, Custom }; + + SoupNetworkProxySettings() = default; + + explicit SoupNetworkProxySettings(Mode proxyMode) + : mode(proxyMode) + { + } + + SoupNetworkProxySettings(const WebCore::SoupNetworkProxySettings& other) + : mode(other.mode) + , defaultProxyURL(other.defaultProxyURL) + , ignoreHosts(g_strdupv(other.ignoreHosts.get())) + , proxyMap(other.proxyMap) + { + } + + SoupNetworkProxySettings& operator=(const WebCore::SoupNetworkProxySettings& other) + { + mode = other.mode; + defaultProxyURL = other.defaultProxyURL; + ignoreHosts.reset(g_strdupv(other.ignoreHosts.get())); + proxyMap = other.proxyMap; + return *this; + } + + bool isEmpty() const { return mode == Mode::Custom && defaultProxyURL.isNull() && !ignoreHosts && proxyMap.isEmpty(); } + + Mode mode { Mode::Default }; + CString defaultProxyURL; + GUniquePtr<char*> ignoreHosts; + HashMap<CString, CString> proxyMap; +}; + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp b/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp index 715e39f04..7c17e604e 100644 --- a/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp +++ b/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -24,23 +24,32 @@ */ #include "config.h" + +#if USE(SOUP) + #include "SoupNetworkSession.h" #include "AuthenticationChallenge.h" -#include "CookieJarSoup.h" +#include "FileSystem.h" #include "GUniquePtrSoup.h" #include "Logging.h" #include "ResourceHandle.h" +#include "SoupNetworkProxySettings.h" +#include <glib/gstdio.h> #include <libsoup/soup.h> +#include <pal/crypto/CryptoDigest.h> +#include <wtf/HashSet.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/text/Base64.h> #include <wtf/text/CString.h> -#include <wtf/text/StringBuilder.h> - -#if PLATFORM(EFL) -#include "ProxyResolverSoup.h" -#endif namespace WebCore { +static bool gIgnoreTLSErrors; +static CString gInitialAcceptLanguages; +static SoupNetworkProxySettings gProxySettings; +static GType gCustomProtocolRequestType; + #if !LOG_DISABLED inline static void soupLogPrinter(SoupLogger*, SoupLoggerLogLevel, char direction, const char* data, gpointer) { @@ -48,37 +57,53 @@ inline static void soupLogPrinter(SoupLogger*, SoupLoggerLogLevel, char directio } #endif -SoupNetworkSession& SoupNetworkSession::defaultSession() -{ - static SoupNetworkSession networkSession(soupCookieJar()); - return networkSession; -} +class HostTLSCertificateSet { +public: + void add(GTlsCertificate* certificate) + { + String certificateHash = computeCertificateHash(certificate); + if (!certificateHash.isEmpty()) + m_certificates.add(certificateHash); + } -std::unique_ptr<SoupNetworkSession> SoupNetworkSession::createPrivateBrowsingSession() -{ - return std::unique_ptr<SoupNetworkSession>(new SoupNetworkSession(soupCookieJar())); -} + bool contains(GTlsCertificate* certificate) const + { + return m_certificates.contains(computeCertificateHash(certificate)); + } -std::unique_ptr<SoupNetworkSession> SoupNetworkSession::createTestingSession() -{ - GRefPtr<SoupCookieJar> cookieJar = adoptGRef(createPrivateBrowsingCookieJar()); - return std::unique_ptr<SoupNetworkSession>(new SoupNetworkSession(cookieJar.get())); -} +private: + static String computeCertificateHash(GTlsCertificate* certificate) + { + GRefPtr<GByteArray> certificateData; + g_object_get(G_OBJECT(certificate), "certificate", &certificateData.outPtr(), nullptr); + if (!certificateData) + return String(); + + auto digest = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_256); + digest->addBytes(certificateData->data, certificateData->len); + + auto hash = digest->computeHash(); + return base64Encode(reinterpret_cast<const char*>(hash.data()), hash.size()); + } -std::unique_ptr<SoupNetworkSession> SoupNetworkSession::createForSoupSession(SoupSession* soupSession) + HashSet<String> m_certificates; +}; + +static HashMap<String, HostTLSCertificateSet, ASCIICaseInsensitiveHash>& clientCertificates() { - return std::unique_ptr<SoupNetworkSession>(new SoupNetworkSession(soupSession)); + static NeverDestroyed<HashMap<String, HostTLSCertificateSet, ASCIICaseInsensitiveHash>> certificates; + return certificates; } -static void authenticateCallback(SoupSession* session, SoupMessage* soupMessage, SoupAuth* soupAuth, gboolean retrying) +static void authenticateCallback(SoupSession*, SoupMessage* soupMessage, SoupAuth* soupAuth, gboolean retrying) { RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(G_OBJECT(soupMessage), "handle")); if (!handle) return; - handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(session, soupMessage, soupAuth, retrying, handle.get())); + handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(soupMessage, soupAuth, retrying, handle.get())); } -#if ENABLE(WEB_TIMING) +#if ENABLE(WEB_TIMING) && !SOUP_CHECK_VERSION(2, 49, 91) static void requestStartedCallback(SoupSession*, SoupMessage* soupMessage, SoupSocket*, gpointer) { RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(G_OBJECT(soupMessage), "handle")); @@ -95,33 +120,50 @@ SoupNetworkSession::SoupNetworkSession(SoupCookieJar* cookieJar) // the rule "Do What Every Other Modern Browser Is Doing". They seem // to significantly improve page loading time compared to soup's // default values. - static const int maxConnections = 35; + static const int maxConnections = 17; static const int maxConnectionsPerHost = 6; + GRefPtr<SoupCookieJar> jar = cookieJar; + if (!jar) { + jar = adoptGRef(soup_cookie_jar_new()); + soup_cookie_jar_set_accept_policy(jar.get(), SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); + } + g_object_set(m_soupSession.get(), SOUP_SESSION_MAX_CONNS, maxConnections, SOUP_SESSION_MAX_CONNS_PER_HOST, maxConnectionsPerHost, SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER, SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_SNIFFER, SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_DEFAULT, - SOUP_SESSION_ADD_FEATURE, cookieJar, + SOUP_SESSION_ADD_FEATURE, jar.get(), SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, + SOUP_SESSION_SSL_STRICT, FALSE, nullptr); + setupCustomProtocols(); + + if (!gInitialAcceptLanguages.isNull()) + setAcceptLanguages(gInitialAcceptLanguages); + +#if SOUP_CHECK_VERSION(2, 53, 92) + if (soup_auth_negotiate_supported()) { + g_object_set(m_soupSession.get(), + SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_NEGOTIATE, + nullptr); + } +#endif + + if (gProxySettings.mode != SoupNetworkProxySettings::Mode::Default) + setupProxy(); setupLogger(); g_signal_connect(m_soupSession.get(), "authenticate", G_CALLBACK(authenticateCallback), nullptr); -#if ENABLE(WEB_TIMING) +#if ENABLE(WEB_TIMING) && !SOUP_CHECK_VERSION(2, 49, 91) g_signal_connect(m_soupSession.get(), "request-started", G_CALLBACK(requestStartedCallback), nullptr); #endif } -SoupNetworkSession::SoupNetworkSession(SoupSession* soupSession) - : m_soupSession(soupSession) -{ - setupLogger(); -} - SoupNetworkSession::~SoupNetworkSession() { } @@ -150,134 +192,137 @@ SoupCookieJar* SoupNetworkSession::cookieJar() const return SOUP_COOKIE_JAR(soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_COOKIE_JAR)); } -void SoupNetworkSession::setCache(SoupCache* cache) +static inline bool stringIsNumeric(const char* str) { - ASSERT(!soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_CACHE)); - soup_session_add_feature(m_soupSession.get(), SOUP_SESSION_FEATURE(cache)); + while (*str) { + if (!g_ascii_isdigit(*str)) + return false; + str++; + } + return true; } -SoupCache* SoupNetworkSession::cache() const +// Old versions of WebKit created this cache. +void SoupNetworkSession::clearOldSoupCache(const String& cacheDirectory) { - SoupSessionFeature* soupCache = soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_CACHE); - return soupCache ? SOUP_CACHE(soupCache) : nullptr; -} + CString cachePath = fileSystemRepresentation(cacheDirectory); + GUniquePtr<char> cacheFile(g_build_filename(cachePath.data(), "soup.cache2", nullptr)); + if (!g_file_test(cacheFile.get(), G_FILE_TEST_IS_REGULAR)) + return; -void SoupNetworkSession::setSSLPolicy(SSLPolicy flags) -{ - g_object_set(m_soupSession.get(), - SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, flags & SSLUseSystemCAFile ? TRUE : FALSE, - SOUP_SESSION_SSL_STRICT, flags & SSLStrict ? TRUE : FALSE, - nullptr); -} + GUniquePtr<GDir> dir(g_dir_open(cachePath.data(), 0, nullptr)); + if (!dir) + return; -SoupNetworkSession::SSLPolicy SoupNetworkSession::sslPolicy() const -{ - gboolean useSystemCAFile, strict; - g_object_get(m_soupSession.get(), - SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, &useSystemCAFile, - SOUP_SESSION_SSL_STRICT, &strict, - nullptr); + while (const char* name = g_dir_read_name(dir.get())) { + if (!g_str_has_prefix(name, "soup.cache") && !stringIsNumeric(name)) + continue; - SSLPolicy flags = 0; - if (useSystemCAFile) - flags |= SSLUseSystemCAFile; - if (strict) - flags |= SSLStrict; - return flags; + GUniquePtr<gchar> filename(g_build_filename(cachePath.data(), name, nullptr)); + if (g_file_test(filename.get(), G_FILE_TEST_IS_REGULAR)) + g_unlink(filename.get()); + } } -void SoupNetworkSession::setHTTPProxy(const char* httpProxy, const char* httpProxyExceptions) +void SoupNetworkSession::setupProxy() { -#if PLATFORM(EFL) - // Only for EFL because GTK port uses the default resolver, which uses GIO's proxy resolver. - if (!httpProxy) { - soup_session_remove_feature_by_type(m_soupSession.get(), SOUP_TYPE_PROXY_URI_RESOLVER); - return; + GRefPtr<GProxyResolver> resolver; + switch (gProxySettings.mode) { + case SoupNetworkProxySettings::Mode::Default: { + GRefPtr<GProxyResolver> currentResolver; + g_object_get(m_soupSession.get(), SOUP_SESSION_PROXY_RESOLVER, ¤tResolver.outPtr(), nullptr); + GProxyResolver* defaultResolver = g_proxy_resolver_get_default(); + if (currentResolver.get() == defaultResolver) + return; + resolver = defaultResolver; + break; + } + case SoupNetworkProxySettings::Mode::NoProxy: + // Do nothing in this case, resolver is nullptr so that when set it will disable proxies. + break; + case SoupNetworkProxySettings::Mode::Custom: + resolver = adoptGRef(g_simple_proxy_resolver_new(nullptr, nullptr)); + if (!gProxySettings.defaultProxyURL.isNull()) + g_simple_proxy_resolver_set_default_proxy(G_SIMPLE_PROXY_RESOLVER(resolver.get()), gProxySettings.defaultProxyURL.data()); + if (gProxySettings.ignoreHosts) + g_simple_proxy_resolver_set_ignore_hosts(G_SIMPLE_PROXY_RESOLVER(resolver.get()), gProxySettings.ignoreHosts.get()); + for (const auto& iter : gProxySettings.proxyMap) + g_simple_proxy_resolver_set_uri_proxy(G_SIMPLE_PROXY_RESOLVER(resolver.get()), iter.key.data(), iter.value.data()); + break; } - GRefPtr<SoupProxyURIResolver> resolver = adoptGRef(soupProxyResolverWkNew(httpProxy, httpProxyExceptions)); - soup_session_add_feature(m_soupSession.get(), SOUP_SESSION_FEATURE(resolver.get())); -#else - UNUSED_PARAM(httpProxy); - UNUSED_PARAM(httpProxyExceptions); -#endif + g_object_set(m_soupSession.get(), SOUP_SESSION_PROXY_RESOLVER, resolver.get(), nullptr); + soup_session_abort(m_soupSession.get()); } -char* SoupNetworkSession::httpProxy() const +void SoupNetworkSession::setProxySettings(const SoupNetworkProxySettings& settings) { -#if PLATFORM(EFL) - SoupSessionFeature* soupResolver = soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_PROXY_URI_RESOLVER); - if (!soupResolver) - return nullptr; + gProxySettings = settings; +} - GUniqueOutPtr<SoupURI> uri; - g_object_get(soupResolver, SOUP_PROXY_RESOLVER_WK_PROXY_URI, &uri.outPtr(), nullptr); +void SoupNetworkSession::setInitialAcceptLanguages(const CString& languages) +{ + gInitialAcceptLanguages = languages; +} - return uri ? soup_uri_to_string(uri.get(), FALSE) : nullptr; -#else - return nullptr; -#endif +void SoupNetworkSession::setAcceptLanguages(const CString& languages) +{ + g_object_set(m_soupSession.get(), "accept-language", languages.data(), nullptr); } -void SoupNetworkSession::setupHTTPProxyFromEnvironment() +void SoupNetworkSession::setCustomProtocolRequestType(GType requestType) { -#if PLATFORM(EFL) - const char* httpProxy = getenv("http_proxy"); - if (!httpProxy) + ASSERT(g_type_is_a(requestType, SOUP_TYPE_REQUEST)); + gCustomProtocolRequestType = requestType; +} + +void SoupNetworkSession::setupCustomProtocols() +{ + if (!g_type_is_a(gCustomProtocolRequestType, SOUP_TYPE_REQUEST)) return; - setHTTPProxy(httpProxy, getenv("no_proxy")); -#endif + auto* requestClass = static_cast<SoupRequestClass*>(g_type_class_peek(gCustomProtocolRequestType)); + if (!requestClass || !requestClass->schemes) + return; + + soup_session_add_feature_by_type(m_soupSession.get(), gCustomProtocolRequestType); } -static CString buildAcceptLanguages(const Vector<String>& languages) +void SoupNetworkSession::setShouldIgnoreTLSErrors(bool ignoreTLSErrors) { - size_t languagesCount = languages.size(); - - // Ignore "C" locale. - size_t cLocalePosition = languages.find("c"); - if (cLocalePosition != notFound) - languagesCount--; - - // Fallback to "en" if the list is empty. - if (!languagesCount) - return "en"; - - // Calculate deltas for the quality values. - int delta; - if (languagesCount < 10) - delta = 10; - else if (languagesCount < 20) - delta = 5; - else - delta = 1; - - // Set quality values for each language. - StringBuilder builder; - for (size_t i = 0; i < languages.size(); ++i) { - if (i == cLocalePosition) - continue; + gIgnoreTLSErrors = ignoreTLSErrors; +} - if (i) - builder.appendLiteral(", "); +void SoupNetworkSession::checkTLSErrors(SoupRequest* soupRequest, SoupMessage* message, std::function<void (const ResourceError&)>&& completionHandler) +{ + if (gIgnoreTLSErrors) { + completionHandler({ }); + return; + } - builder.append(languages[i]); + GTlsCertificate* certificate = nullptr; + GTlsCertificateFlags tlsErrors = static_cast<GTlsCertificateFlags>(0); + soup_message_get_https_status(message, &certificate, &tlsErrors); + if (!tlsErrors) { + completionHandler({ }); + return; + } - int quality = 100 - i * delta; - if (quality > 0 && quality < 100) { - char buffer[8]; - g_ascii_formatd(buffer, 8, "%.2f", quality / 100.0); - builder.append(String::format(";q=%s", buffer)); - } + URL url(soup_request_get_uri(soupRequest)); + auto it = clientCertificates().find(url.host()); + if (it != clientCertificates().end() && it->value.contains(certificate)) { + completionHandler({ }); + return; } - return builder.toString().utf8(); + completionHandler(ResourceError::tlsError(soupRequest, tlsErrors, certificate)); } -void SoupNetworkSession::setAcceptLanguages(const Vector<String>& languages) +void SoupNetworkSession::allowSpecificHTTPSCertificateForHost(const CertificateInfo& certificateInfo, const String& host) { - g_object_set(m_soupSession.get(), "accept-language", buildAcceptLanguages(languages).data(), nullptr); + clientCertificates().add(host, HostTLSCertificateSet()).iterator->value.add(certificateInfo.certificate()); } } // namespace WebCore +#endif diff --git a/Source/WebCore/platform/network/soup/SoupNetworkSession.h b/Source/WebCore/platform/network/soup/SoupNetworkSession.h index b9801f9f9..908b6cb6d 100644 --- a/Source/WebCore/platform/network/soup/SoupNetworkSession.h +++ b/Source/WebCore/platform/network/soup/SoupNetworkSession.h @@ -10,10 +10,10 @@ * 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 + * 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 COMPUTER, INC. OR + * 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 @@ -26,54 +26,52 @@ #ifndef SoupNetworkSession_h #define SoupNetworkSession_h +#include <functional> +#include <glib-object.h> #include <wtf/Noncopyable.h> #include <wtf/Vector.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> #include <wtf/text/WTFString.h> typedef struct _SoupCache SoupCache; typedef struct _SoupCookieJar SoupCookieJar; +typedef struct _SoupMessage SoupMessage; +typedef struct _SoupRequest SoupRequest; typedef struct _SoupSession SoupSession; namespace WebCore { +class CertificateInfo; +class ResourceError; +struct SoupNetworkProxySettings; + class SoupNetworkSession { WTF_MAKE_NONCOPYABLE(SoupNetworkSession); WTF_MAKE_FAST_ALLOCATED; public: + explicit SoupNetworkSession(SoupCookieJar* = nullptr); ~SoupNetworkSession(); - static SoupNetworkSession& defaultSession(); - static std::unique_ptr<SoupNetworkSession> createPrivateBrowsingSession(); - static std::unique_ptr<SoupNetworkSession> createTestingSession(); - static std::unique_ptr<SoupNetworkSession> createForSoupSession(SoupSession*); - - enum SSLPolicyFlags { - SSLStrict = 1 << 0, - SSLUseSystemCAFile = 1 << 1 - }; - typedef unsigned SSLPolicy; - SoupSession* soupSession() const { return m_soupSession.get(); } void setCookieJar(SoupCookieJar*); SoupCookieJar* cookieJar() const; - void setCache(SoupCache*); - SoupCache* cache() const; + static void clearOldSoupCache(const String& cacheDirectory); - void setSSLPolicy(SSLPolicy); - SSLPolicy sslPolicy() const; + static void setProxySettings(const SoupNetworkProxySettings&); + void setupProxy(); - void setHTTPProxy(const char* httpProxy, const char* httpProxyExceptions); - char* httpProxy() const; - void setupHTTPProxyFromEnvironment(); + static void setInitialAcceptLanguages(const CString&); + void setAcceptLanguages(const CString&); - void setAcceptLanguages(const Vector<String>&); + static void setShouldIgnoreTLSErrors(bool); + static void checkTLSErrors(SoupRequest*, SoupMessage*, std::function<void (const ResourceError&)>&&); + static void allowSpecificHTTPSCertificateForHost(const CertificateInfo&, const String& host); -private: - SoupNetworkSession(SoupCookieJar*); - SoupNetworkSession(SoupSession*); + static void setCustomProtocolRequestType(GType); + void setupCustomProtocols(); +private: void setupLogger(); GRefPtr<SoupSession> m_soupSession; diff --git a/Source/WebCore/platform/network/soup/SynchronousLoaderClientSoup.cpp b/Source/WebCore/platform/network/soup/SynchronousLoaderClientSoup.cpp index cfc01aed7..09e42d8e9 100644 --- a/Source/WebCore/platform/network/soup/SynchronousLoaderClientSoup.cpp +++ b/Source/WebCore/platform/network/soup/SynchronousLoaderClientSoup.cpp @@ -24,6 +24,9 @@ */ #include "config.h" + +#if USE(SOUP) + #include "SynchronousLoaderClient.h" #include "AuthenticationChallenge.h" @@ -44,3 +47,6 @@ ResourceError SynchronousLoaderClient::platformBadResponseError() } } + +#endif + diff --git a/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.cpp b/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.cpp new file mode 100644 index 000000000..ec1325842 --- /dev/null +++ b/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2012 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 "WebKitSoupRequestGeneric.h" + +#include "ResourceRequest.h" +#include <wtf/text/CString.h> + +using namespace WebCore; + +G_DEFINE_TYPE(WebKitSoupRequestGeneric, webkit_soup_request_generic, SOUP_TYPE_REQUEST) + +struct _WebKitSoupRequestGenericPrivate { + CString mimeType; + goffset contentLength; + ResourceRequest resourceRequest; +}; + +static void webkitSoupRequestGenericFinalize(GObject* object) +{ + WEBKIT_SOUP_REQUEST_GENERIC(object)->priv->~WebKitSoupRequestGenericPrivate(); + G_OBJECT_CLASS(webkit_soup_request_generic_parent_class)->finalize(object); +} + +static void webkit_soup_request_generic_init(WebKitSoupRequestGeneric* request) +{ + WebKitSoupRequestGenericPrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(request, WEBKIT_TYPE_SOUP_REQUEST_GENERIC, WebKitSoupRequestGenericPrivate); + request->priv = priv; + new (priv) WebKitSoupRequestGenericPrivate(); +} + +static void webkitSoupRequestGenericSendAsync(SoupRequest* request, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) +{ + WebKitSoupRequestGenericClient* client = WEBKIT_SOUP_REQUEST_GENERIC_GET_CLASS(request)->client; + ASSERT(client); + client->startRequest(adoptGRef(g_task_new(request, cancellable, callback, userData))); +} + +static GInputStream* webkitSoupRequestGenericSendFinish(SoupRequest* request, GAsyncResult* result, GError** error) +{ + g_return_val_if_fail(g_task_is_valid(result, request), nullptr); + auto* inputStream = g_task_propagate_pointer(G_TASK(result), error); + return inputStream ? G_INPUT_STREAM(inputStream) : nullptr; +} + +static goffset webkitSoupRequestGenericGetContentLength(SoupRequest* request) +{ + return WEBKIT_SOUP_REQUEST_GENERIC(request)->priv->contentLength; +} + +static const char* webkitSoupRequestGenericGetContentType(SoupRequest* request) +{ + return WEBKIT_SOUP_REQUEST_GENERIC(request)->priv->mimeType.data(); +} + +static void webkit_soup_request_generic_class_init(WebKitSoupRequestGenericClass* requestGenericClass) +{ + GObjectClass* gObjectClass = G_OBJECT_CLASS(requestGenericClass); + gObjectClass->finalize = webkitSoupRequestGenericFinalize; + + SoupRequestClass* requestClass = SOUP_REQUEST_CLASS(requestGenericClass); + requestClass->send_async = webkitSoupRequestGenericSendAsync; + requestClass->send_finish = webkitSoupRequestGenericSendFinish; + requestClass->get_content_length = webkitSoupRequestGenericGetContentLength; + requestClass->get_content_type = webkitSoupRequestGenericGetContentType; + + g_type_class_add_private(requestGenericClass, sizeof(WebKitSoupRequestGenericPrivate)); +} + +void webkitSoupRequestGenericSetContentLength(WebKitSoupRequestGeneric* request, goffset contentLength) +{ + request->priv->contentLength = contentLength; +} + +void webkitSoupRequestGenericSetContentType(WebKitSoupRequestGeneric* request, const char* mimeType) +{ + request->priv->mimeType = mimeType; +} + +void webkitSoupRequestGenericSetRequest(WebKitSoupRequestGeneric* request, const ResourceRequest& resourceRequest) +{ + request->priv->resourceRequest = resourceRequest; +} + +const ResourceRequest& webkitSoupRequestGenericGetRequest(WebKitSoupRequestGeneric* request) +{ + return request->priv->resourceRequest; +} diff --git a/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.h b/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.h new file mode 100644 index 000000000..09cbd2725 --- /dev/null +++ b/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 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 WebKitSoupRequestGeneric_h +#define WebKitSoupRequestGeneric_h + +#include "WebKitSoupRequestGenericClient.h" +#include <glib-object.h> +#include <libsoup/soup.h> + +G_BEGIN_DECLS + +namespace WebCore { +class ResourceRequest; +} + +#define WEBKIT_TYPE_SOUP_REQUEST_GENERIC (webkit_soup_request_generic_get_type()) +#define WEBKIT_SOUP_REQUEST_GENERIC(object) (G_TYPE_CHECK_INSTANCE_CAST((object), WEBKIT_TYPE_SOUP_REQUEST_GENERIC, WebKitSoupRequestGeneric)) +#define WEBKIT_IS_SOUP_REQUEST_GENERIC(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), WEBKIT_TYPE_SOUP_REQUEST_GENERIC)) +#define WEBKIT_SOUP_REQUEST_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_SOUP_REQUEST_GENERIC, WebKitSoupRequestGenericClass)) +#define WEBKIT_IS_SOUP_REQUEST_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_SOUP_REQUEST_GENERIC)) +#define WEBKIT_SOUP_REQUEST_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WEBKIT_TYPE_SOUP_REQUEST_GENERIC, WebKitSoupRequestGenericClass)) + +typedef struct _WebKitSoupRequestGeneric WebKitSoupRequestGeneric; +typedef struct _WebKitSoupRequestGenericClass WebKitSoupRequestGenericClass; +typedef struct _WebKitSoupRequestGenericPrivate WebKitSoupRequestGenericPrivate; + +struct _WebKitSoupRequestGeneric { + SoupRequest parent; + + WebKitSoupRequestGenericPrivate *priv; +}; + +struct _WebKitSoupRequestGenericClass { + SoupRequestClass parent; + + WebCore::WebKitSoupRequestGenericClient* client; +}; + +GType webkit_soup_request_generic_get_type(); + +void webkitSoupRequestGenericSetContentLength(WebKitSoupRequestGeneric*, goffset contentLength); +void webkitSoupRequestGenericSetContentType(WebKitSoupRequestGeneric*, const char* mimeType); +void webkitSoupRequestGenericSetRequest(WebKitSoupRequestGeneric*, const WebCore::ResourceRequest&); +const WebCore::ResourceRequest& webkitSoupRequestGenericGetRequest(WebKitSoupRequestGeneric*); + +G_END_DECLS + +#endif // WebKitSoupRequestGeneric_h diff --git a/Source/WebCore/platform/network/soup/WebKitSoupRequestGenericClient.h b/Source/WebCore/platform/network/soup/WebKitSoupRequestGenericClient.h new file mode 100644 index 000000000..db470a67f --- /dev/null +++ b/Source/WebCore/platform/network/soup/WebKitSoupRequestGenericClient.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <wtf/glib/GRefPtr.h> + +typedef struct _GTask GTask; + +namespace WebCore { + +class WebKitSoupRequestGenericClient { +public: + virtual void startRequest(GRefPtr<GTask>&&) = 0; +}; + +} // namespace WebCore + |