diff options
author | Steve Block <steveblock@google.com> | 2011-05-18 13:36:51 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-24 15:38:28 +0100 |
commit | 2fc2651226baac27029e38c9d6ef883fa32084db (patch) | |
tree | e396d4bf89dcce6ed02071be66212495b1df1dec /Source/WebCore/platform/network | |
parent | b3725cedeb43722b3b175aaeff70552e562d2c94 (diff) | |
download | external_webkit-2fc2651226baac27029e38c9d6ef883fa32084db.zip external_webkit-2fc2651226baac27029e38c9d6ef883fa32084db.tar.gz external_webkit-2fc2651226baac27029e38c9d6ef883fa32084db.tar.bz2 |
Merge WebKit at r78450: Initial merge by git.
Change-Id: I6d3e5f1f868ec266a0aafdef66182ddc3f265dc1
Diffstat (limited to 'Source/WebCore/platform/network')
54 files changed, 692 insertions, 5133 deletions
diff --git a/Source/WebCore/platform/network/BlobRegistryImpl.cpp b/Source/WebCore/platform/network/BlobRegistryImpl.cpp index 2c4e8fa..83517f1 100644 --- a/Source/WebCore/platform/network/BlobRegistryImpl.cpp +++ b/Source/WebCore/platform/network/BlobRegistryImpl.cpp @@ -68,7 +68,9 @@ PassRefPtr<ResourceHandle> BlobRegistryImpl::createResourceHandle(const Resource if (!shouldLoadResource(request)) return 0; - return BlobResourceHandle::create(m_blobs.get(request.url().string()), request, client); + RefPtr<BlobResourceHandle> handle = BlobResourceHandle::create(m_blobs.get(request.url().string()), request, client); + handle->start(); + return handle.release(); } bool BlobRegistryImpl::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) diff --git a/Source/WebCore/platform/network/BlobResourceHandle.cpp b/Source/WebCore/platform/network/BlobResourceHandle.cpp index 753052a..24c9088 100644 --- a/Source/WebCore/platform/network/BlobResourceHandle.cpp +++ b/Source/WebCore/platform/network/BlobResourceHandle.cpp @@ -138,11 +138,6 @@ void BlobResourceHandle::loadResourceSynchronously(PassRefPtr<BlobStorageData> b handle->start(); } -static void delayedStart(void* context) -{ - static_cast<BlobResourceHandle*>(context)->start(); -} - BlobResourceHandle::BlobResourceHandle(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async) : ResourceHandle(request, client, false, false) , m_blobData(blobData) @@ -158,11 +153,9 @@ BlobResourceHandle::BlobResourceHandle(PassRefPtr<BlobStorageData> blobData, con , m_readItemCount(0) , m_fileOpened(false) { - if (m_async) { - // We need to take a ref. + if (m_async) m_asyncStream = client->createAsyncFileStream(this); - callOnMainThread(delayedStart, this); - } else + else m_stream = FileStream::create(); } @@ -187,10 +180,32 @@ void BlobResourceHandle::cancel() } m_aborted = true; + + ResourceHandle::cancel(); +} + +void delayedStartBlobResourceHandle(void* context) +{ + RefPtr<BlobResourceHandle> handle = adoptRef(static_cast<BlobResourceHandle*>(context)); + handle->doStart(); } void BlobResourceHandle::start() { + if (m_async) { + // Keep BlobResourceHandle alive until delayedStartBlobResourceHandle runs. + ref(); + + // Finish this async call quickly and return. + callOnMainThread(delayedStartBlobResourceHandle, this); + return; + } + + doStart(); +} + +void BlobResourceHandle::doStart() +{ // Do not continue if the request is aborted or an error occurs. if (m_aborted || m_errorCode) return; @@ -578,10 +593,23 @@ void BlobResourceHandle::notifyFail(int errorCode) client()->didFail(this, ResourceError(String(), errorCode, firstRequest().url(), String())); } +static void doNotifyFinish(void* context) +{ + BlobResourceHandle* handle = static_cast<BlobResourceHandle*>(context); + if (handle->client()) + handle->client()->didFinishLoading(handle, 0); +} + void BlobResourceHandle::notifyFinish() { - if (client()) - client()->didFinishLoading(this, 0); + 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); + return; + } + + doNotifyFinish(this); } } // namespace WebCore diff --git a/Source/WebCore/platform/network/BlobResourceHandle.h b/Source/WebCore/platform/network/BlobResourceHandle.h index 63e8578..1e9e94a 100644 --- a/Source/WebCore/platform/network/BlobResourceHandle.h +++ b/Source/WebCore/platform/network/BlobResourceHandle.h @@ -69,9 +69,12 @@ public: int readSync(char*, int); private: + friend void delayedStartBlobResourceHandle(void*); + BlobResourceHandle(PassRefPtr<BlobStorageData>, const ResourceRequest&, ResourceHandleClient*, bool async); virtual ~BlobResourceHandle(); + void doStart(); void getSizeForNext(); void seek(); void consumeData(const char* data, int bytesRead); diff --git a/Source/WebCore/platform/network/FormData.cpp b/Source/WebCore/platform/network/FormData.cpp index 16f98ad..9784b7f 100644 --- a/Source/WebCore/platform/network/FormData.cpp +++ b/Source/WebCore/platform/network/FormData.cpp @@ -209,14 +209,16 @@ void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncod name = file->webkitRelativePath().isEmpty() ? file->name() : file->webkitRelativePath(); #else name = file->name(); -#endif - +#endif // Let the application specify a filename if it's going to generate a replacement file for the upload. - if (Page* page = document->page()) { - String generatedFileName; - shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(file->path(), generatedFileName); - if (shouldGenerateFile) - name = generatedFileName; + const String& path = file->path(); + if (!path.isEmpty()) { + if (Page* page = document->page()) { + String generatedFileName; + shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFileName); + if (shouldGenerateFile) + name = generatedFileName; + } } } else { // For non-file blob, use the identifier part of the URL as the name. @@ -364,7 +366,9 @@ static void encode(Encoder& encoder, const FormDataElement& element) static bool decode(Decoder& decoder, FormDataElement& element) { - uint32_t type = element.m_type; + uint32_t type; + if (!decoder.decodeUInt32(type)) + return false; switch (type) { case FormDataElement::data: { @@ -432,7 +436,7 @@ void FormData::encodeForBackForward(Encoder& encoder) const encoder.encodeBool(m_hasGeneratedFiles); - encoder.encodeBool(m_identifier); + encoder.encodeInt64(m_identifier); } PassRefPtr<FormData> FormData::decodeForBackForward(Decoder& decoder) diff --git a/Source/WebCore/platform/network/FormDataBuilder.cpp b/Source/WebCore/platform/network/FormDataBuilder.cpp index da28fc2..e973f99 100644 --- a/Source/WebCore/platform/network/FormDataBuilder.cpp +++ b/Source/WebCore/platform/network/FormDataBuilder.cpp @@ -94,10 +94,7 @@ TextEncoding FormDataBuilder::encodingFromAcceptCharset(const String& acceptChar return encoding; } - if (Frame* frame = document->frame()) - return frame->loader()->writer()->encoding(); - - return Latin1Encoding(); + return document->inputEncoding(); } Vector<char> FormDataBuilder::generateUniqueBoundaryString() diff --git a/Source/WebCore/platform/network/ProtectionSpaceHash.h b/Source/WebCore/platform/network/ProtectionSpaceHash.h index 08716b5..9934321 100644 --- a/Source/WebCore/platform/network/ProtectionSpaceHash.h +++ b/Source/WebCore/platform/network/ProtectionSpaceHash.h @@ -42,11 +42,11 @@ struct ProtectionSpaceHash { protectionSpace.realm().impl() ? protectionSpace.realm().impl()->hash() : 0 }; - unsigned codeCount = sizeof(hashCodes) / sizeof(UChar); + unsigned codeCount = sizeof(hashCodes); // Ignore realm for proxies. if (protectionSpace.isProxy()) - codeCount -= sizeof(hashCodes[0]) / sizeof(UChar); - return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), codeCount); + codeCount -= sizeof(hashCodes[0]); + return WTF::StringHasher::createBlobHash(hashCodes, codeCount); } static bool equal(const ProtectionSpace& a, const ProtectionSpace& b) { return a == b; } diff --git a/Source/WebCore/platform/network/ResourceHandle.h b/Source/WebCore/platform/network/ResourceHandle.h index bb94b59..c2a0b8e 100644 --- a/Source/WebCore/platform/network/ResourceHandle.h +++ b/Source/WebCore/platform/network/ResourceHandle.h @@ -41,7 +41,7 @@ typedef struct _SoupSession SoupSession; typedef const struct __CFData * CFDataRef; #endif -#if PLATFORM(WIN) +#if USE(WININET) typedef unsigned long DWORD; typedef unsigned long DWORD_PTR; typedef void* LPVOID; @@ -173,7 +173,7 @@ public: bool hasAuthenticationChallenge() const; void clearAuthentication(); - void cancel(); + virtual void cancel(); // The client may be 0, in which case no callbacks will be made. ResourceHandleClient* client() const; diff --git a/Source/WebCore/platform/network/ResourceHandleInternal.h b/Source/WebCore/platform/network/ResourceHandleInternal.h index ed66944..5512062 100644 --- a/Source/WebCore/platform/network/ResourceHandleInternal.h +++ b/Source/WebCore/platform/network/ResourceHandleInternal.h @@ -46,8 +46,9 @@ #endif #if USE(SOUP) -#include "soup-requester.h" #include <GRefPtr.h> +#define LIBSOUP_USE_UNSTABLE_REQUEST_API +#include <libsoup/soup-request.h> #include <libsoup/soup.h> class Frame; #endif @@ -114,6 +115,8 @@ namespace WebCore { , m_cancelled(false) , m_buffer(0) , m_total(0) + , m_bodySize(0) + , m_bodyDataSent(0) , m_idleHandler(0) , m_gotChunkHandler(0) #endif @@ -132,9 +135,6 @@ namespace WebCore { m_user = url.user(); m_pass = url.pass(); m_firstRequest.removeCredentials(); -#if USE(SOUP) - m_requester = adoptGRef(webkit_soup_requester_new()); -#endif } ~ResourceHandleInternal(); @@ -190,12 +190,13 @@ namespace WebCore { GRefPtr<SoupMessage> m_soupMessage; ResourceResponse m_response; bool m_cancelled; - GRefPtr<WebKitSoupRequest> m_soupRequest; - GRefPtr<WebKitSoupRequester> m_requester; + GRefPtr<SoupRequest> m_soupRequest; GRefPtr<GInputStream> m_inputStream; GRefPtr<GCancellable> m_cancellable; char* m_buffer; gsize m_total; + unsigned long m_bodySize; + unsigned long m_bodyDataSent; guint m_idleHandler; RefPtr<NetworkingContext> m_context; gulong m_gotChunkHandler; diff --git a/Source/WebCore/platform/network/ResourceRequestBase.cpp b/Source/WebCore/platform/network/ResourceRequestBase.cpp index ae8316a..ba58461 100644 --- a/Source/WebCore/platform/network/ResourceRequestBase.cpp +++ b/Source/WebCore/platform/network/ResourceRequestBase.cpp @@ -45,6 +45,7 @@ PassOwnPtr<ResourceRequest> ResourceRequestBase::adopt(PassOwnPtr<CrossThreadRes request->setTimeoutInterval(data->m_timeoutInterval); request->setFirstPartyForCookies(data->m_firstPartyForCookies); request->setHTTPMethod(data->m_httpMethod); + request->setPriority(data->m_priority); request->setTargetType(data->m_targetType); request->updateResourceRequest(); @@ -78,6 +79,7 @@ PassOwnPtr<CrossThreadResourceRequestData> ResourceRequestBase::copyData() const data->m_firstPartyForCookies = firstPartyForCookies().copy(); data->m_httpMethod = httpMethod().crossThreadString(); data->m_httpHeaders = httpHeaderFields().copyData(); + data->m_priority = priority(); data->m_targetType = m_targetType; data->m_responseContentDispositionEncodingFallbackArray.reserveInitialCapacity(m_responseContentDispositionEncodingFallbackArray.size()); @@ -314,6 +316,23 @@ void ResourceRequestBase::setAllowCookies(bool allowCookies) m_platformRequestUpdated = false; } +ResourceLoadPriority ResourceRequestBase::priority() const +{ + updateResourceRequest(); + + return m_priority; +} + +void ResourceRequestBase::setPriority(ResourceLoadPriority priority) +{ + updateResourceRequest(); + + m_priority = priority; + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + void ResourceRequestBase::addHTTPHeaderField(const AtomicString& name, const String& value) { updateResourceRequest(); @@ -352,6 +371,9 @@ bool equalIgnoringHeaderFields(const ResourceRequestBase& a, const ResourceReque if (a.allowCookies() != b.allowCookies()) return false; + if (a.priority() != b.priority()) + return false; + FormData* formDataA = a.httpBody(); FormData* formDataB = b.httpBody(); diff --git a/Source/WebCore/platform/network/ResourceRequestBase.h b/Source/WebCore/platform/network/ResourceRequestBase.h index dce33db..9cc9148 100644 --- a/Source/WebCore/platform/network/ResourceRequestBase.h +++ b/Source/WebCore/platform/network/ResourceRequestBase.h @@ -29,8 +29,9 @@ #define ResourceRequestBase_h #include "FormData.h" -#include "KURL.h" #include "HTTPHeaderMap.h" +#include "KURL.h" +#include "ResourceLoadPriority.h" #include <wtf/OwnPtr.h> @@ -128,6 +129,9 @@ namespace WebCore { bool allowCookies() const; void setAllowCookies(bool allowCookies); + ResourceLoadPriority priority() const; + void setPriority(ResourceLoadPriority); + bool isConditional() const; // Whether the associated ResourceHandleClient needs to be notified of @@ -157,6 +161,7 @@ namespace WebCore { , m_reportUploadProgress(false) , m_reportLoadTiming(false) , m_reportRawHeaders(false) + , m_priority(ResourceLoadPriorityLow) , m_targetType(TargetIsSubresource) { } @@ -172,6 +177,7 @@ namespace WebCore { , m_reportUploadProgress(false) , m_reportLoadTiming(false) , m_reportRawHeaders(false) + , m_priority(ResourceLoadPriorityLow) , m_targetType(TargetIsSubresource) { } @@ -197,6 +203,7 @@ namespace WebCore { bool m_reportUploadProgress; bool m_reportLoadTiming; bool m_reportRawHeaders; + ResourceLoadPriority m_priority; TargetType m_targetType; private: @@ -223,11 +230,20 @@ namespace WebCore { Vector<String> m_responseContentDispositionEncodingFallbackArray; RefPtr<FormData> m_httpBody; bool m_allowCookies; + ResourceLoadPriority m_priority; ResourceRequestBase::TargetType m_targetType; }; unsigned initializeMaximumHTTPConnectionCountPerHost(); +#if PLATFORM(CF) + bool isHTTPPipeliningEnabled(); + bool shouldForceHTTPPipeliningPriorityHigh(); +#else + inline bool isHTTPPipeliningEnabled() { return false; } + inline bool shouldForceHTTPPipeliningPriorityHigh() { return false; } +#endif + } // namespace WebCore #endif // ResourceRequestBase_h diff --git a/Source/WebCore/platform/network/cf/DNSCFNet.cpp b/Source/WebCore/platform/network/cf/DNSCFNet.cpp index 166abbf..b6f9922 100644 --- a/Source/WebCore/platform/network/cf/DNSCFNet.cpp +++ b/Source/WebCore/platform/network/cf/DNSCFNet.cpp @@ -36,6 +36,7 @@ #if PLATFORM(WIN) #include "LoaderRunLoopCF.h" +#include <CFNetwork/CFNetwork.h> #endif #if defined(BUILDING_ON_LEOPARD) diff --git a/Source/WebCore/platform/network/cf/DownloadBundle.h b/Source/WebCore/platform/network/cf/DownloadBundle.h new file mode 100644 index 0000000..cf90908 --- /dev/null +++ b/Source/WebCore/platform/network/cf/DownloadBundle.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DownloadBundle_h +#define DownloadBundle_h + +#include <wtf/Forward.h> + +typedef const struct __CFData* CFDataRef; + +namespace WebCore { +namespace DownloadBundle { + +bool appendResumeData(CFDataRef resumeData, const String& bundlePath); +CFDataRef extractResumeData(const String& bundlePath); +const String& fileExtension(); + +} // namespace DownloadBundle +} // namespace WebCore + +#endif // DownloadBundle_h diff --git a/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp b/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp index eb0ec3a..8bc8f08 100644 --- a/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp +++ b/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp @@ -145,17 +145,8 @@ static void advanceCurrentStream(FormStreamFields *form) char* data = nextInput.m_data.releaseBuffer(); form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull); form->currentData = data; - } else { - CFStringRef filename = nextInput.m_filename.createCFString(); -#if PLATFORM(WIN) - CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLWindowsPathStyle, FALSE); -#else - CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLPOSIXPathStyle, FALSE); -#endif - CFRelease(filename); - form->currentStream = CFReadStreamCreateWithFile(0, fileURL); - CFRelease(fileURL); - } + } else + form->currentStream = CFReadStreamCreateWithFile(0, pathAsURL(nextInput.m_filename).get()); form->remainingElements.removeLast(); // Set up the callback. diff --git a/Source/WebCore/platform/network/cf/LoaderRunLoopCF.cpp b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.cpp index 249fe43..a7cc639 100644 --- a/Source/WebCore/platform/network/cf/LoaderRunLoopCF.cpp +++ b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.cpp @@ -28,6 +28,7 @@ #if USE(CFNETWORK) +#include <CoreFoundation/CoreFoundation.h> #include <wtf/Threading.h> namespace WebCore { diff --git a/Source/WebCore/platform/network/cf/LoaderRunLoopCF.h b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.h index f66128c..e0d3ba4 100644 --- a/Source/WebCore/platform/network/cf/LoaderRunLoopCF.h +++ b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.h @@ -32,6 +32,8 @@ #error This code is not needed on platforms other than Windows, because main thread's CFRunLoop can be used. #endif +typedef struct __CFRunLoop* CFRunLoopRef; + namespace WebCore { CFRunLoopRef loaderRunLoop(); diff --git a/Source/WebCore/platform/network/cf/ProxyServerCFNet.cpp b/Source/WebCore/platform/network/cf/ProxyServerCFNet.cpp index 3bef808..57d714b 100644 --- a/Source/WebCore/platform/network/cf/ProxyServerCFNet.cpp +++ b/Source/WebCore/platform/network/cf/ProxyServerCFNet.cpp @@ -29,6 +29,10 @@ #include "KURL.h" #include <wtf/RetainPtr.h> +#if PLATFORM(WIN) +#include <CFNetwork/CFNetwork.h> +#endif + namespace WebCore { #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) diff --git a/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp b/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp index 66ae5a0..52b100f 100644 --- a/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp +++ b/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp @@ -126,12 +126,7 @@ static void setDefaultMIMEType(CFURLResponseRef response) static String encodeBasicAuthorization(const String& user, const String& password) { - CString unencodedString = (user + ":" + password).utf8(); - Vector<char> unencoded(unencodedString.length()); - std::copy(unencodedString.data(), unencodedString.data() + unencodedString.length(), unencoded.begin()); - Vector<char> encoded; - base64Encode(unencoded, encoded); - return String(encoded.data(), encoded.size()); + return base64Encode((user + ":" + password).utf8()); } CFURLRequestRef willSendRequest(CFURLConnectionRef conn, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo) diff --git a/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp index 410a649..7a1dfd5 100644 --- a/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp +++ b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp @@ -26,16 +26,22 @@ #include "config.h" #include "ResourceRequestCFNet.h" -#if USE(CFNETWORK) - -#include "FormDataStreamCFNet.h" #include "ResourceRequest.h" +#if PLATFORM(MAC) +#include "WebCoreSystemInterface.h" +#endif + +#if USE(CFNETWORK) +#include "FormDataStreamCFNet.h" #include <CFNetwork/CFURLRequestPriv.h> #include <WebKitSystemInterface/WebKitSystemInterface.h> +#endif namespace WebCore { +#if USE(CFNETWORK) + typedef void (*CFURLRequestSetContentDispositionEncodingFallbackArrayFunction)(CFMutableURLRequestRef, CFArrayRef); typedef CFArrayRef (*CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction)(CFURLRequestRef); @@ -189,12 +195,43 @@ void ResourceRequest::doUpdateResourceRequest() m_httpBody = httpBodyFromRequest(m_cfRequest.get()); } +#endif // USE(CFNETWORK) + unsigned initializeMaximumHTTPConnectionCountPerHost() { static const unsigned preferredConnectionCount = 6; - return wkInitializeMaximumHTTPConnectionCountPerHost(preferredConnectionCount); + static const unsigned unlimitedConnectionCount = 10000; + + // Always set the connection count per host, even when pipelining. + unsigned maximumHTTPConnectionCountPerHost = wkInitializeMaximumHTTPConnectionCountPerHost(preferredConnectionCount); + +#if PLATFORM(MAC) + if (isHTTPPipeliningEnabled()) { + // When pipelining do not rate-limit requests sent from WebCore since CFNetwork handles that. + return unlimitedConnectionCount; + } +#endif + + return maximumHTTPConnectionCountPerHost; } -} // namespace WebCore +static inline bool readBooleanPreference(CFStringRef key) +{ + Boolean keyExistsAndHasValidFormat; + Boolean result = CFPreferencesGetAppBooleanValue(key, kCFPreferencesCurrentApplication, &keyExistsAndHasValidFormat); + return keyExistsAndHasValidFormat ? result : false; +} -#endif // USE(CFNETWORK) +bool isHTTPPipeliningEnabled() +{ + static bool isEnabled = readBooleanPreference(CFSTR("WebKitEnableHTTPPipelining")); + return isEnabled; +} + +bool shouldForceHTTPPipeliningPriorityHigh() +{ + static bool shouldForcePriorityHigh = readBooleanPreference(CFSTR("WebKitForceHTTPPipeliningPriorityHigh")); + return shouldForcePriorityHigh; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/cf/ResourceRequestCFNet.h b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.h index 39587a4..09f4cea 100644 --- a/Source/WebCore/platform/network/cf/ResourceRequestCFNet.h +++ b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.h @@ -26,18 +26,55 @@ #ifndef ResourceRequestCFNet_h #define ResourceRequestCFNet_h -#if USE(CFNETWORK) +#include "ResourceLoadPriority.h" +#if USE(CFNETWORK) typedef const struct _CFURLRequest* CFURLRequestRef; +#endif namespace WebCore { - class ResourceRequest; +class ResourceRequest; + +#if USE(CFNETWORK) +void getResourceRequest(ResourceRequest&, CFURLRequestRef); +CFURLRequestRef cfURLRequest(const ResourceRequest&); +#endif + +inline ResourceLoadPriority mapHTTPPipeliningPriorityToResourceLoadPriority(int priority) +{ + switch (priority) { + case 0: + return ResourceLoadPriorityLow; + case 1: + return ResourceLoadPriorityMedium; + case 2: + return ResourceLoadPriorityHigh; + default: + ASSERT_NOT_REACHED(); + return ResourceLoadPriorityLowest; + } +} + +inline int mapResourceLoadPriorityToHTTPPipeliningPriority(ResourceLoadPriority priority) +{ + switch (priority) { + case ResourceLoadPriorityVeryLow: + case ResourceLoadPriorityLow: + return 0; + case ResourceLoadPriorityMedium: + return 1; + case ResourceLoadPriorityHigh: + return 2; + case ResourceLoadPriorityUnresolved: + ASSERT_NOT_REACHED(); + return 0; + } - void getResourceRequest(ResourceRequest&, CFURLRequestRef); - CFURLRequestRef cfURLRequest(const ResourceRequest&); + ASSERT_NOT_REACHED(); + return 0; } -#endif // USE(CFNETWORK) +} // namespace WebCore #endif // ResourceRequestCFNet_h diff --git a/Source/WebCore/platform/network/cf/SocketStreamHandle.h b/Source/WebCore/platform/network/cf/SocketStreamHandle.h index df1d4a3..fbda3bc 100644 --- a/Source/WebCore/platform/network/cf/SocketStreamHandle.h +++ b/Source/WebCore/platform/network/cf/SocketStreamHandle.h @@ -36,6 +36,7 @@ #include "SocketStreamHandleBase.h" #include <wtf/RetainPtr.h> +typedef struct __CFHTTPMessage* CFHTTPMessageRef; namespace WebCore { diff --git a/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp b/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp index 821b1ca..06454a7 100644 --- a/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp +++ b/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp @@ -47,6 +47,7 @@ #if PLATFORM(WIN) #include "LoaderRunLoopCF.h" +#include <CFNetwork/CFNetwork.h> #include <WebKitSystemInterface/WebKitSystemInterface.h> #else #include "WebCoreSystemInterface.h" diff --git a/Source/WebCore/platform/network/chromium/ResourceRequest.cpp b/Source/WebCore/platform/network/chromium/ResourceRequest.cpp index 69591c1..519c63f 100644 --- a/Source/WebCore/platform/network/chromium/ResourceRequest.cpp +++ b/Source/WebCore/platform/network/chromium/ResourceRequest.cpp @@ -43,6 +43,7 @@ PassOwnPtr<CrossThreadResourceRequestData> ResourceRequest::doPlatformCopyData(P data->m_requestorProcessID = m_requestorProcessID; data->m_appCacheHostID = m_appCacheHostID; data->m_hasUserGesture = m_hasUserGesture; + data->m_downloadToFile = m_downloadToFile; return data; } @@ -52,6 +53,7 @@ void ResourceRequest::doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData> m_requestorProcessID = data->m_requestorProcessID; m_appCacheHostID = data->m_appCacheHostID; m_hasUserGesture = data->m_hasUserGesture; + m_downloadToFile = data->m_downloadToFile; } } // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/ResourceRequest.h b/Source/WebCore/platform/network/chromium/ResourceRequest.h index 41ad6e0..07b31eb 100644 --- a/Source/WebCore/platform/network/chromium/ResourceRequest.h +++ b/Source/WebCore/platform/network/chromium/ResourceRequest.h @@ -42,6 +42,7 @@ namespace WebCore { , m_requestorProcessID(0) , m_appCacheHostID(0) , m_hasUserGesture(false) + , m_downloadToFile(false) { } @@ -51,6 +52,7 @@ namespace WebCore { , m_requestorProcessID(0) , m_appCacheHostID(0) , m_hasUserGesture(false) + , m_downloadToFile(false) { } @@ -60,6 +62,7 @@ namespace WebCore { , m_requestorProcessID(0) , m_appCacheHostID(0) , m_hasUserGesture(false) + , m_downloadToFile(false) { setHTTPReferrer(referrer); } @@ -70,6 +73,7 @@ namespace WebCore { , m_requestorProcessID(0) , m_appCacheHostID(0) , m_hasUserGesture(false) + , m_downloadToFile(false) { } @@ -92,6 +96,10 @@ namespace WebCore { bool hasUserGesture() const { return m_hasUserGesture; } void setHasUserGesture(bool hasUserGesture) { m_hasUserGesture = hasUserGesture; } + // True if request should be downloaded to file. + bool downloadToFile() const { return m_downloadToFile; } + void setDownloadToFile(bool downloadToFile) { m_downloadToFile = downloadToFile; } + private: friend class ResourceRequestBase; @@ -105,6 +113,7 @@ namespace WebCore { int m_requestorProcessID; int m_appCacheHostID; bool m_hasUserGesture; + bool m_downloadToFile; }; struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { @@ -112,6 +121,7 @@ namespace WebCore { int m_requestorProcessID; int m_appCacheHostID; bool m_hasUserGesture; + bool m_downloadToFile; }; } // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/ResourceResponse.cpp b/Source/WebCore/platform/network/chromium/ResourceResponse.cpp index acd44d3..fc8ac62 100644 --- a/Source/WebCore/platform/network/chromium/ResourceResponse.cpp +++ b/Source/WebCore/platform/network/chromium/ResourceResponse.cpp @@ -39,6 +39,8 @@ PassOwnPtr<CrossThreadResourceResponseData> ResourceResponse::doPlatformCopyData data->m_wasAlternateProtocolAvailable = m_wasAlternateProtocolAvailable; data->m_wasFetchedViaProxy = m_wasFetchedViaProxy; data->m_responseTime = m_responseTime; + data->m_socketAddress = m_socketAddress; + data->m_downloadFilePath = m_downloadFilePath; return data; } @@ -53,6 +55,8 @@ void ResourceResponse::doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseDat m_wasAlternateProtocolAvailable = data->m_wasAlternateProtocolAvailable; m_wasFetchedViaProxy = data->m_wasFetchedViaProxy; m_responseTime = data->m_responseTime; + m_socketAddress = data->m_socketAddress; + m_downloadFilePath = data->m_downloadFilePath; } } // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/ResourceResponse.h b/Source/WebCore/platform/network/chromium/ResourceResponse.h index 5e99994..35f13d1 100644 --- a/Source/WebCore/platform/network/chromium/ResourceResponse.h +++ b/Source/WebCore/platform/network/chromium/ResourceResponse.h @@ -96,6 +96,12 @@ namespace WebCore { double responseTime() const { return m_responseTime; } void setResponseTime(double responseTime) { m_responseTime = responseTime; } + const String& socketAddress() const { return m_socketAddress; } + void setSocketAddress(const String& value) { m_socketAddress = value; } + + const String& downloadFilePath() const { return m_downloadFilePath; } + void setDownloadFilePath(const String& downloadFilePath) { m_downloadFilePath = downloadFilePath; } + private: friend class ResourceResponseBase; @@ -143,6 +149,13 @@ namespace WebCore { // The time at which the response headers were received. For cached // responses, this time could be "far" in the past. double m_responseTime; + + // Remote address of the socket which fetched this resource, for presenting + // to inquisitive users. Can be "ipv4:port", "[ipv6]:port", or empty. + String m_socketAddress; + + // The path to the downloaded file. + String m_downloadFilePath; }; struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { @@ -155,6 +168,8 @@ namespace WebCore { bool m_wasAlternateProtocolAvailable; bool m_wasFetchedViaProxy; double m_responseTime; + String m_socketAddress; + String m_downloadFilePath; }; } // namespace WebCore diff --git a/Source/WebCore/platform/network/mac/AuthenticationMac.mm b/Source/WebCore/platform/network/mac/AuthenticationMac.mm index efa42d9..a187187 100644 --- a/Source/WebCore/platform/network/mac/AuthenticationMac.mm +++ b/Source/WebCore/platform/network/mac/AuthenticationMac.mm @@ -31,6 +31,7 @@ #import "AuthenticationClient.h" #import "Credential.h" #import "ProtectionSpace.h" +#import <wtf/UnusedParam.h> #import <Foundation/NSURLAuthenticationChallenge.h> #import <Foundation/NSURLCredential.h> @@ -86,6 +87,20 @@ using namespace WebCore; m_client->receivedCancellation(core(challenge)); } +- (void)performDefaultHandlingForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + // FIXME: <rdar://problem/8995483> Determine what, if anything, we should do here. + ASSERT_NOT_REACHED(); + UNUSED_PARAM(challenge); +} + +- (void)rejectProtectionSpaceAndContinueWithChallenge:(NSURLAuthenticationChallenge *)challenge +{ + // FIXME: <rdar://problem/8995483> Determine what, if anything, we should do here. + ASSERT_NOT_REACHED(); + UNUSED_PARAM(challenge); +} + @end namespace WebCore { diff --git a/Source/WebCore/platform/network/mac/FormDataStreamMac.mm b/Source/WebCore/platform/network/mac/FormDataStreamMac.mm index 03f4579..eb6f601 100644 --- a/Source/WebCore/platform/network/mac/FormDataStreamMac.mm +++ b/Source/WebCore/platform/network/mac/FormDataStreamMac.mm @@ -185,9 +185,7 @@ static bool advanceCurrentStream(FormStreamFields* form) } #endif const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename; - RetainPtr<CFStringRef> filename(AdoptCF, path.createCFString()); - RetainPtr<CFURLRef> fileURL(AdoptCF, CFURLCreateWithFileSystemPath(0, filename.get(), kCFURLPOSIXPathStyle, FALSE)); - form->currentStream = CFReadStreamCreateWithFile(0, fileURL.get()); + form->currentStream = CFReadStreamCreateWithFile(0, pathAsURL(path).get()); if (!form->currentStream) { // The file must have been removed or become unreadable. return false; diff --git a/Source/WebCore/platform/network/mac/ResourceHandleMac.mm b/Source/WebCore/platform/network/mac/ResourceHandleMac.mm index 84b656c..2d687c0 100644 --- a/Source/WebCore/platform/network/mac/ResourceHandleMac.mm +++ b/Source/WebCore/platform/network/mac/ResourceHandleMac.mm @@ -164,12 +164,7 @@ public: #ifndef BUILDING_ON_TIGER static String encodeBasicAuthorization(const String& user, const String& password) { - CString unencodedString = (user + ":" + password).utf8(); - Vector<char> unencoded(unencodedString.length()); - std::copy(unencodedString.data(), unencodedString.data() + unencodedString.length(), unencoded.begin()); - Vector<char> encoded; - base64Encode(unencoded, encoded); - return String(encoded.data(), encoded.size()); + return base64Encode((user + ":" + password).utf8()); } #endif diff --git a/Source/WebCore/platform/network/mac/ResourceRequestMac.mm b/Source/WebCore/platform/network/mac/ResourceRequestMac.mm index f0357e5..640d237 100644 --- a/Source/WebCore/platform/network/mac/ResourceRequestMac.mm +++ b/Source/WebCore/platform/network/mac/ResourceRequestMac.mm @@ -31,6 +31,8 @@ #import "WebCoreSystemInterface.h" #import "FormDataStreamMac.h" +#import "ResourceRequestCFNet.h" +#import "WebCoreSystemInterface.h" #import <Foundation/Foundation.h> @@ -65,7 +67,12 @@ void ResourceRequest::doUpdateResourceRequest() if (NSString* method = [m_nsRequest.get() HTTPMethod]) m_httpMethod = method; m_allowCookies = [m_nsRequest.get() HTTPShouldHandleCookies]; - + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + if (isHTTPPipeliningEnabled() && !shouldForceHTTPPipeliningPriorityHigh()) + m_priority = mapHTTPPipeliningPriorityToResourceLoadPriority(wkGetHTTPPipeliningPriority(m_nsRequest.get())); +#endif + NSDictionary *headers = [m_nsRequest.get() allHTTPHeaderFields]; NSEnumerator *e = [headers keyEnumerator]; NSString *name; @@ -111,6 +118,13 @@ void ResourceRequest::doUpdatePlatformRequest() wkSupportsMultipartXMixedReplace(nsRequest); #endif +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + if (isHTTPPipeliningEnabled()) { + int priority = mapResourceLoadPriorityToHTTPPipeliningPriority(m_priority); + wkSetHTTPPipeliningPriority(nsRequest, shouldForceHTTPPipeliningPriorityHigh() ? 2 : priority); + } +#endif + [nsRequest setCachePolicy:(NSURLRequestCachePolicy)cachePolicy()]; if (timeoutInterval() != unspecifiedTimeoutInterval) [nsRequest setTimeoutInterval:timeoutInterval()]; @@ -154,12 +168,6 @@ void ResourceRequest::applyWebArchiveHackForMail() // Hack because Mail checks for this property to detect data / archive loads [NSURLProtocol setProperty:@"" forKey:@"WebDataRequest" inRequest:(NSMutableURLRequest *)nsURLRequest()]; } - -unsigned initializeMaximumHTTPConnectionCountPerHost() -{ - static const unsigned preferredConnectionCount = 6; - return wkInitializeMaximumHTTPConnectionCountPerHost(preferredConnectionCount); -} } // namespace WebCore diff --git a/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp index e0d6e69..a98b4f4 100644 --- a/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp +++ b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp @@ -35,6 +35,18 @@ QtNAMThreadSafeProxy::QtNAMThreadSafeProxy(QNetworkAccessManager *manager) connect(this, SIGNAL(localSetCookiesRequested(const QUrl&, const QString&)), SLOT(localSetCookies(const QUrl&, const QString&))); connect(this, SIGNAL(localCookiesForUrlRequested(const QUrl&, bool*, QList<QNetworkCookie>*)), SLOT(localCookiesForUrl(const QUrl&, bool*, QList<QNetworkCookie>*))); connect(this, SIGNAL(localWillLoadFromCacheRequested(const QUrl&, bool*, bool*)), SLOT(localWillLoadFromCache(const QUrl&, bool*, bool*))); + connect(this, SIGNAL(hasCookieJarRequested(bool*, bool*)), SLOT(hasCookieJar(bool*, bool*))); +} + +bool QtNAMThreadSafeProxy::hasCookieJar() +{ + bool result; + bool done = false; + emit hasCookieJarRequested(&done, &result); + QMutexLocker lock(&m_resultMutex); + while (!done) + m_resultWaitCondition.wait(&m_resultMutex); + return result; } void QtNAMThreadSafeProxy::localSetCookies(const QUrl& url, const QString& cookies) @@ -69,6 +81,14 @@ void QtNAMThreadSafeProxy::localWillLoadFromCache(const QUrl& url, bool* done, b m_resultWaitCondition.wakeAll(); } +void QtNAMThreadSafeProxy::hasCookieJar(bool* done, bool* result) +{ + QMutexLocker lock(&m_resultMutex); + *result = !!m_manager->cookieJar(); + *done = true; + m_resultWaitCondition.wakeAll(); +} + QtNetworkReplyThreadSafeProxy::QtNetworkReplyThreadSafeProxy(QNetworkAccessManager *manager) : m_manager(manager) , m_reply(0) diff --git a/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h index ae963cf..4906fe2 100644 --- a/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h +++ b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h @@ -67,15 +67,19 @@ public: return result; } + bool hasCookieJar(); + signals: void localSetCookiesRequested(const QUrl&, const QString& cookies); void localCookiesForUrlRequested(const QUrl&, bool* done, QList<QNetworkCookie>* result); void localWillLoadFromCacheRequested(const QUrl&, bool* done, bool* result); + void hasCookieJarRequested(bool* done, bool* result); private slots: void localSetCookies(const QUrl&, const QString& cookies); void localCookiesForUrl(const QUrl&, bool* done, QList<QNetworkCookie>* result); void localWillLoadFromCache(const QUrl&, bool* done, bool* result); + void hasCookieJar(bool* done, bool* result); private: QNetworkAccessManager* m_manager; diff --git a/Source/WebCore/platform/network/soup/CookieJarSoup.cpp b/Source/WebCore/platform/network/soup/CookieJarSoup.cpp index ba29622..e2c2f05 100644 --- a/Source/WebCore/platform/network/soup/CookieJarSoup.cpp +++ b/Source/WebCore/platform/network/soup/CookieJarSoup.cpp @@ -38,9 +38,7 @@ SoupCookieJar* defaultCookieJar() cookiesInitialized = true; cookieJar = soup_cookie_jar_new(); -#ifdef HAVE_LIBSOUP_2_29_90 soup_cookie_jar_set_accept_policy(cookieJar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); -#endif } return cookieJar; @@ -67,18 +65,12 @@ void setCookies(Document* document, const KURL& url, const String& value) GOwnPtr<SoupURI> origin(soup_uri_new(url.string().utf8().data())); -#ifdef HAVE_LIBSOUP_2_29_90 GOwnPtr<SoupURI> firstParty(soup_uri_new(document->firstPartyForCookies().string().utf8().data())); soup_cookie_jar_set_cookie_with_first_party(jar, origin.get(), firstParty.get(), value.utf8().data()); -#else - soup_cookie_jar_set_cookie(jar, - origin.get(), - value.utf8().data()); -#endif } String cookies(const Document* /*document*/, const KURL& url) diff --git a/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp index a7170fe..3b1f157 100644 --- a/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -45,12 +45,14 @@ #include "ResourceHandleInternal.h" #include "ResourceResponse.h" #include "SharedBuffer.h" -#include "soup-request-http.h" #include "TextEncoding.h" #include <errno.h> #include <fcntl.h> #include <gio/gio.h> #include <glib.h> +#define LIBSOUP_USE_UNSTABLE_REQUEST_API +#include <libsoup/soup-request-http.h> +#include <libsoup/soup-requester.h> #include <libsoup/soup.h> #include <sys/stat.h> #include <sys/types.h> @@ -127,7 +129,7 @@ static void cleanupSoupRequestOperation(ResourceHandle*, bool isDestroying); static void sendRequestCallback(GObject*, GAsyncResult*, gpointer); static void readCallback(GObject*, GAsyncResult*, gpointer); static void closeCallback(GObject*, GAsyncResult*, gpointer); -static bool startGio(ResourceHandle*, KURL); +static bool startNonHTTPRequest(ResourceHandle*, KURL); ResourceHandleInternal::~ResourceHandleInternal() { @@ -145,14 +147,48 @@ ResourceHandle::~ResourceHandle() cleanupSoupRequestOperation(this, true); } +static void ensureSessionIsInitialized(SoupSession* session) +{ + // Values taken from http://stevesouders.com/ua/index.php following + // the rule "Do What Every Other Modern Browser Is Doing". They seem + // to significantly improve page loading time compared to soup's + // default values. + static const int maxConnections = 60; + static const int maxConnectionsPerHost = 6; + + if (g_object_get_data(G_OBJECT(session), "webkit-init")) + return; + + SoupCookieJar* jar = SOUP_COOKIE_JAR(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR)); + if (!jar) + soup_session_add_feature(session, SOUP_SESSION_FEATURE(defaultCookieJar())); + else + setDefaultCookieJar(jar); + + if (!soup_session_get_feature(session, SOUP_TYPE_LOGGER) && LogNetwork.state == WTFLogChannelOn) { + SoupLogger* logger = soup_logger_new(static_cast<SoupLoggerLogLevel>(SOUP_LOGGER_LOG_BODY), -1); + soup_session_add_feature(session, SOUP_SESSION_FEATURE(logger)); + g_object_unref(logger); + } + + SoupRequester* requester = soup_requester_new(); + soup_session_add_feature(session, SOUP_SESSION_FEATURE(requester)); + g_object_unref(requester); + + g_object_set(session, + SOUP_SESSION_MAX_CONNS, maxConnections, + SOUP_SESSION_MAX_CONNS_PER_HOST, maxConnectionsPerHost, + NULL); + + g_object_set_data(G_OBJECT(session), "webkit-init", reinterpret_cast<void*>(0xdeadbeef)); +} + void ResourceHandle::prepareForURL(const KURL &url) { -#ifdef HAVE_LIBSOUP_2_29_90 GOwnPtr<SoupURI> soupURI(soup_uri_new(url.prettyURL().utf8().data())); if (!soupURI) return; soup_session_prepare_for_uri(ResourceHandle::defaultSession(), soupURI.get()); -#endif } // All other kinds of redirections, except for the *304* status code @@ -206,14 +242,12 @@ static void restartedCallback(SoupMessage* msg, gpointer data) if (d->m_cancelled) return; -#ifdef HAVE_LIBSOUP_2_29_90 // Update the first party in case the base URL changed with the redirect String firstPartyString = request.firstPartyForCookies().string(); if (!firstPartyString.isEmpty()) { GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data())); soup_message_set_first_party(d->m_soupMessage.get(), firstParty.get()); } -#endif } static void contentSniffedCallback(SoupMessage*, const char*, GHashTable*, gpointer); @@ -263,7 +297,26 @@ static void gotHeadersCallback(SoupMessage* msg, gpointer data) client->didReceiveResponse(handle.get(), d->m_response); } -// This callback will not be called if the content sniffer is disabled in startHttp. +static void wroteBodyDataCallback(SoupMessage*, SoupBuffer* buffer, gpointer data) +{ + RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); + if (!handle) + return; + + ASSERT(buffer); + ResourceHandleInternal* internal = handle->getInternal(); + internal->m_bodyDataSent += buffer->length; + + if (internal->m_cancelled) + return; + ResourceHandleClient* client = handle->client(); + if (!client) + return; + + client->didSendData(handle.get(), internal->m_bodyDataSent, internal->m_bodySize); +} + +// This callback will not be called if the content sniffer is disabled in startHTTPRequest. static void contentSniffedCallback(SoupMessage* msg, const char* sniffedType, GHashTable *params, gpointer data) { if (sniffedType) { @@ -312,136 +365,11 @@ static void gotChunkCallback(SoupMessage* msg, SoupBuffer* chunk, gpointer data) client->didReceiveData(handle.get(), chunk->data, chunk->length, false); } -static gboolean parseDataUrl(gpointer callbackData) -{ - ResourceHandle* handle = static_cast<ResourceHandle*>(callbackData); - ResourceHandleClient* client = handle->client(); - ResourceHandleInternal* d = handle->getInternal(); - if (d->m_cancelled) - return false; - - d->m_idleHandler = 0; - - ASSERT(client); - if (!client) - return false; - - String url = handle->firstRequest().url().string(); - ASSERT(url.startsWith("data:", false)); - - int index = url.find(','); - if (index == -1) { - client->cannotShowURL(handle); - return false; - } - - String mediaType = url.substring(5, index - 5); - - bool isBase64 = mediaType.endsWith(";base64", false); - if (isBase64) - mediaType = mediaType.left(mediaType.length() - 7); - - if (mediaType.isEmpty()) - mediaType = "text/plain;charset=US-ASCII"; - - String mimeType = extractMIMETypeFromMediaType(mediaType); - String charset = extractCharsetFromMediaType(mediaType); - - ASSERT(d->m_response.isNull()); - - d->m_response.setURL(handle->firstRequest().url()); - d->m_response.setMimeType(mimeType); - - // For non base64 encoded data we have to convert to UTF-16 early - // due to limitations in KURL - d->m_response.setTextEncodingName(isBase64 ? charset : "UTF-16"); - client->didReceiveResponse(handle, d->m_response); - - // The load may be cancelled, and the client may be destroyed - // by any of the client reporting calls, so we check, and bail - // out in either of those cases. - if (d->m_cancelled || !handle->client()) - return false; - - SoupSession* session = handle->defaultSession(); - GOwnPtr<GError> error; - d->m_soupRequest = adoptGRef(webkit_soup_requester_request(d->m_requester.get(), handle->firstRequest().url().string().utf8().data(), session, &error.outPtr())); - if (error) { - d->m_soupRequest = 0; - client->didFinishLoading(handle, 0); - return false; - } - - d->m_inputStream = adoptGRef(webkit_soup_request_send(d->m_soupRequest.get(), 0, &error.outPtr())); - if (error) { - d->m_inputStream = 0; - client->didFinishLoading(handle, 0); - return false; - } - - d->m_buffer = static_cast<char*>(g_slice_alloc0(READ_BUFFER_SIZE)); - d->m_total = 0; - - g_object_set_data(G_OBJECT(d->m_inputStream.get()), "webkit-resource", handle); - // balanced by a deref() in cleanupSoupRequestOperation, which should always run - handle->ref(); - - d->m_cancellable = adoptGRef(g_cancellable_new()); - g_input_stream_read_async(d->m_inputStream.get(), d->m_buffer, READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, - d->m_cancellable.get(), readCallback, GINT_TO_POINTER(!isBase64)); - - return false; -} - -static bool startData(ResourceHandle* handle, String urlString) -{ - ASSERT(handle); - - ResourceHandleInternal* d = handle->getInternal(); - - // If parseDataUrl is called synchronously the job is not yet effectively started - // and webkit won't never know that the data has been parsed even didFinishLoading is called. - d->m_idleHandler = g_timeout_add(0, parseDataUrl, handle); - return true; -} - static SoupSession* createSoupSession() { return soup_session_async_new(); } -// Values taken from http://stevesouders.com/ua/index.php following -// the rule "Do What Every Other Modern Browser Is Doing". They seem -// to significantly improve page loading time compared to soup's -// default values. -#define MAX_CONNECTIONS 60 -#define MAX_CONNECTIONS_PER_HOST 6 - -static void ensureSessionIsInitialized(SoupSession* session) -{ - if (g_object_get_data(G_OBJECT(session), "webkit-init")) - return; - - SoupCookieJar* jar = reinterpret_cast<SoupCookieJar*>(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR)); - if (!jar) - soup_session_add_feature(session, SOUP_SESSION_FEATURE(defaultCookieJar())); - else - setDefaultCookieJar(jar); - - if (!soup_session_get_feature(session, SOUP_TYPE_LOGGER) && LogNetwork.state == WTFLogChannelOn) { - SoupLogger* logger = soup_logger_new(static_cast<SoupLoggerLogLevel>(SOUP_LOGGER_LOG_BODY), -1); - soup_logger_attach(logger, session); - g_object_unref(logger); - } - - g_object_set(session, - SOUP_SESSION_MAX_CONNS, MAX_CONNECTIONS, - SOUP_SESSION_MAX_CONNS_PER_HOST, MAX_CONNECTIONS_PER_HOST, - NULL); - - g_object_set_data(G_OBJECT(session), "webkit-init", reinterpret_cast<void*>(0xdeadbeef)); -} - static void cleanupSoupRequestOperation(ResourceHandle* handle, bool isDestroying = false) { ResourceHandleInternal* d = handle->getInternal(); @@ -495,14 +423,14 @@ static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer use } GOwnPtr<GError> error; - GInputStream* in = webkit_soup_request_send_finish(d->m_soupRequest.get(), res, &error.outPtr()); + GInputStream* in = soup_request_send_finish(d->m_soupRequest.get(), res, &error.outPtr()); if (error) { SoupMessage* soupMsg = d->m_soupMessage.get(); gboolean isTransportError = d->m_soupMessage && SOUP_STATUS_IS_TRANSPORT_ERROR(soupMsg->status_code); if (isTransportError || (error->domain == G_IO_ERROR)) { - SoupURI* uri = webkit_soup_request_get_uri(d->m_soupRequest.get()); + SoupURI* uri = soup_request_get_uri(d->m_soupRequest.get()); GOwnPtr<char> uriStr(soup_uri_to_string(uri, false)); gint errorCode = isTransportError ? static_cast<gint>(soupMsg->status_code) : error->code; const gchar* errorMsg = isTransportError ? soupMsg->reason_phrase : error->message; @@ -550,13 +478,14 @@ static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer use // readCallback needs it g_object_set_data(G_OBJECT(d->m_inputStream.get()), "webkit-resource", handle.get()); - // Ensure a response is sent for any protocols that don't explicitly support responses - // through got-headers signal or content sniffing. - // (e.g. file and GIO based protocol). - if (!handle->shouldContentSniff() && d->m_response.isNull()) { + // If not using SoupMessage we need to call didReceiveResponse now. + // (This will change later when SoupRequest supports content sniffing.) + if (!d->m_soupMessage) { d->m_response.setURL(handle->firstRequest().url()); - d->m_response.setMimeType(webkit_soup_request_get_content_type(d->m_soupRequest.get())); - d->m_response.setExpectedContentLength(webkit_soup_request_get_content_length(d->m_soupRequest.get())); + const gchar* contentType = soup_request_get_content_type(d->m_soupRequest.get()); + d->m_response.setMimeType(extractMIMETypeFromMediaType(contentType)); + d->m_response.setTextEncodingName(extractCharsetFromMediaType(contentType)); + d->m_response.setExpectedContentLength(soup_request_get_content_length(d->m_soupRequest.get())); client->didReceiveResponse(handle.get(), d->m_response); if (d->m_cancelled) { @@ -572,12 +501,56 @@ static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer use G_PRIORITY_DEFAULT, d->m_cancellable.get(), readCallback, 0); } -static bool startHttp(ResourceHandle* handle) +static bool addFormElementsToSoupMessage(SoupMessage* message, const char* contentType, FormData* httpBody, unsigned long& totalBodySize) +{ + size_t numElements = httpBody->elements().size(); + if (numElements < 2) { // No file upload is the most common case. + Vector<char> body; + httpBody->flatten(body); + totalBodySize = body.size(); + soup_message_set_request(message, contentType, SOUP_MEMORY_COPY, body.data(), body.size()); + return true; + } + + // We have more than one element to upload, and some may be large files, + // which we will want to mmap instead of copying into memory + soup_message_body_set_accumulate(message->request_body, FALSE); + 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; + } + + // This technique is inspired by libsoup's simple-httpd test. + GOwnPtr<GError> error; + CString fileName = fileSystemRepresentation(element.m_filename); + GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr()); + if (error) + return false; + + gsize mappedFileSize = g_mapped_file_get_length(fileMapping); + totalBodySize += mappedFileSize; + SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping), + mappedFileSize, fileMapping, + reinterpret_cast<GDestroyNotify>(g_mapped_file_unref)); + soup_message_body_append_buffer(message->request_body, soupBuffer); + soup_buffer_free(soupBuffer); + } + + return true; +} + +static bool startHTTPRequest(ResourceHandle* handle) { ASSERT(handle); SoupSession* session = handle->defaultSession(); ensureSessionIsInitialized(session); + SoupRequester* requester = SOUP_REQUESTER(soup_session_get_feature(session, SOUP_TYPE_REQUESTER)); ResourceHandleInternal* d = handle->getInternal(); @@ -587,7 +560,7 @@ static bool startHttp(ResourceHandle* handle) request.setURL(url); GOwnPtr<GError> error; - d->m_soupRequest = adoptGRef(webkit_soup_requester_request(d->m_requester.get(), url.string().utf8().data(), session, &error.outPtr())); + d->m_soupRequest = adoptGRef(soup_requester_request(requester, url.string().utf8().data(), &error.outPtr())); if (error) { d->m_soupRequest = 0; return false; @@ -595,7 +568,7 @@ static bool startHttp(ResourceHandle* handle) g_object_set_data(G_OBJECT(d->m_soupRequest.get()), "webkit-resource", handle); - d->m_soupMessage = adoptGRef(webkit_soup_request_http_get_message(WEBKIT_SOUP_REQUEST_HTTP(d->m_soupRequest.get()))); + d->m_soupMessage = adoptGRef(soup_request_http_get_message(SOUP_REQUEST_HTTP(d->m_soupRequest.get()))); if (!d->m_soupMessage) return false; @@ -609,65 +582,23 @@ static bool startHttp(ResourceHandle* handle) g_signal_connect(soupMessage, "restarted", G_CALLBACK(restartedCallback), handle); g_signal_connect(soupMessage, "got-headers", G_CALLBACK(gotHeadersCallback), handle); + g_signal_connect(soupMessage, "wrote-body-data", G_CALLBACK(wroteBodyDataCallback), handle); d->m_gotChunkHandler = g_signal_connect(soupMessage, "got-chunk", G_CALLBACK(gotChunkCallback), handle); -#ifdef HAVE_LIBSOUP_2_29_90 String firstPartyString = request.firstPartyForCookies().string(); if (!firstPartyString.isEmpty()) { GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data())); soup_message_set_first_party(soupMessage, firstParty.get()); } -#endif FormData* httpBody = d->m_firstRequest.httpBody(); - if (httpBody && !httpBody->isEmpty()) { - size_t numElements = httpBody->elements().size(); - - // handle the most common case (i.e. no file upload) - if (numElements < 2) { - Vector<char> body; - httpBody->flatten(body); - soup_message_set_request(soupMessage, d->m_firstRequest.httpContentType().utf8().data(), - SOUP_MEMORY_COPY, body.data(), body.size()); - } else { - /* - * we have more than one element to upload, and some may - * be (big) files, which we will want to mmap instead of - * copying into memory; TODO: support upload of non-local - * (think sftp://) files by using GIO? - */ - soup_message_body_set_accumulate(soupMessage->request_body, FALSE); - for (size_t i = 0; i < numElements; i++) { - const FormDataElement& element = httpBody->elements()[i]; - - if (element.m_type == FormDataElement::data) - soup_message_body_append(soupMessage->request_body, SOUP_MEMORY_TEMPORARY, element.m_data.data(), element.m_data.size()); - else { - /* - * mapping for uploaded files code inspired by technique used in - * libsoup's simple-httpd test - */ - GOwnPtr<GError> error; - CString fileName = fileSystemRepresentation(element.m_filename); - GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr()); - - if (error) { - g_signal_handlers_disconnect_matched(soupMessage, G_SIGNAL_MATCH_DATA, - 0, 0, 0, 0, handle); - d->m_soupMessage.clear(); - - return false; - } - - SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping), - g_mapped_file_get_length(fileMapping), - fileMapping, - reinterpret_cast<GDestroyNotify>(g_mapped_file_unref)); - soup_message_body_append_buffer(soupMessage->request_body, soupBuffer); - soup_buffer_free(soupBuffer); - } - } - } + CString contentType = d->m_firstRequest.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. + g_signal_handlers_disconnect_matched(soupMessage, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, handle); + d->m_soupMessage.clear(); + return false; } // balanced by a deref() in cleanupSoupRequestOperation, which should always run @@ -681,7 +612,7 @@ static bool startHttp(ResourceHandle* handle) // Send the request only if it's not been explicitely deferred. if (!d->m_defersLoading) { d->m_cancellable = adoptGRef(g_cancellable_new()); - webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); } return true; @@ -715,19 +646,13 @@ bool ResourceHandle::start(NetworkingContext* context) // Used to set the authentication dialog toplevel; may be NULL d->m_context = context; - if (equalIgnoringCase(protocol, "data")) - return startData(this, urlString); - if (equalIgnoringCase(protocol, "http") || equalIgnoringCase(protocol, "https")) { - if (startHttp(this)) + if (startHTTPRequest(this)) return true; } - if (equalIgnoringCase(protocol, "file") || equalIgnoringCase(protocol, "ftp") || equalIgnoringCase(protocol, "ftps")) { - // FIXME: should we be doing any other protocols here? - if (startGio(this, url)) - return true; - } + if (startNonHTTPRequest(this, url)) + return true; // Error must not be reported immediately this->scheduleFailure(InvalidURLFailure); @@ -764,7 +689,7 @@ void ResourceHandle::platformSetDefersLoading(bool defersLoading) if (!defersLoading && !d->m_cancellable && d->m_soupRequest.get()) { d->m_cancellable = adoptGRef(g_cancellable_new()); - webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); return; } @@ -795,11 +720,13 @@ bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*) void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials /*storedCredentials*/, ResourceError& error, ResourceResponse& response, Vector<char>& data) { WebCoreSynchronousLoader syncLoader(error, response, data); - // FIXME: we should use the ResourceHandle::create method here, - // but it makes us timeout in a couple of tests. See - // https://bugs.webkit.org/show_bug.cgi?id=41823 - RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, &syncLoader, false /*defersLoading*/, false /*shouldContentSniff*/)); - handle->start(context); + 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(); } @@ -811,18 +738,8 @@ static void closeCallback(GObject* source, GAsyncResult* res, gpointer) return; ResourceHandleInternal* d = handle->getInternal(); - ResourceHandleClient* client = handle->client(); - g_input_stream_close_finish(d->m_inputStream.get(), res, 0); cleanupSoupRequestOperation(handle.get()); - - // The load may have been cancelled, the client may have been - // destroyed already. In such cases calling didFinishLoading is a - // bad idea. - if (d->m_cancelled || !client) - return; - - client->didFinishLoading(handle.get(), 0); } static void readCallback(GObject* source, GAsyncResult* asyncResult, gpointer data) @@ -844,7 +761,7 @@ static void readCallback(GObject* source, GAsyncResult* asyncResult, gpointer da gssize bytesRead = g_input_stream_read_finish(d->m_inputStream.get(), asyncResult, &error.outPtr()); if (error) { - SoupURI* uri = webkit_soup_request_get_uri(d->m_soupRequest.get()); + SoupURI* uri = soup_request_get_uri(d->m_soupRequest.get()); GOwnPtr<char> uriStr(soup_uri_to_string(uri, false)); ResourceError resourceError(g_quark_to_string(G_IO_ERROR), error->code, uriStr.get(), error ? String::fromUTF8(error->message) : String()); @@ -854,6 +771,10 @@ static void readCallback(GObject* source, GAsyncResult* asyncResult, gpointer da } if (!bytesRead) { + // Finish the load. We do not wait for the stream to + // close. Instead we better notify WebCore as soon as possible + client->didFinishLoading(handle.get(), 0); + g_input_stream_close_async(d->m_inputStream.get(), G_PRIORITY_DEFAULT, 0, closeCallback, 0); return; @@ -881,7 +802,7 @@ static void readCallback(GObject* source, GAsyncResult* asyncResult, gpointer da d->m_cancellable.get(), readCallback, data); } -static bool startGio(ResourceHandle* handle, KURL url) +static bool startNonHTTPRequest(ResourceHandle* handle, KURL url) { ASSERT(handle); @@ -889,18 +810,14 @@ static bool startGio(ResourceHandle* handle, KURL url) return false; SoupSession* session = handle->defaultSession(); + ensureSessionIsInitialized(session); + SoupRequester* requester = SOUP_REQUESTER(soup_session_get_feature(session, SOUP_TYPE_REQUESTER)); ResourceHandleInternal* d = handle->getInternal(); - // GIO doesn't know how to handle refs and queries, so remove them - // TODO: use KURL.fileSystemPath after KURLGtk and FileSystemGtk are - // using GIO internally, and providing URIs instead of file paths - url.removeFragmentIdentifier(); - url.setQuery(String()); - url.removePort(); CString urlStr = url.string().utf8(); GOwnPtr<GError> error; - d->m_soupRequest = adoptGRef(webkit_soup_requester_request(d->m_requester.get(), urlStr.data(), session, &error.outPtr())); + d->m_soupRequest = adoptGRef(soup_requester_request(requester, urlStr.data(), &error.outPtr())); if (error) { d->m_soupRequest = 0; return false; @@ -912,7 +829,7 @@ static bool startGio(ResourceHandle* handle, KURL url) handle->ref(); d->m_cancellable = adoptGRef(g_cancellable_new()); - webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); return true; } diff --git a/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp b/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp index d46e47b..5016bf1 100644 --- a/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp @@ -46,13 +46,11 @@ void ResourceRequest::updateSoupMessage(SoupMessage* soupMessage) const soup_message_headers_append(soupHeaders, it->first.string().utf8().data(), it->second.utf8().data()); } -#ifdef HAVE_LIBSOUP_2_29_90 String firstPartyString = firstPartyForCookies().string(); if (!firstPartyString.isEmpty()) { GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data())); soup_message_set_first_party(soupMessage, firstParty.get()); } -#endif soup_message_set_flags(soupMessage, m_soupFlags); } @@ -71,13 +69,11 @@ SoupMessage* ResourceRequest::toSoupMessage() const soup_message_headers_append(soupHeaders, it->first.string().utf8().data(), it->second.utf8().data()); } -#ifdef HAVE_LIBSOUP_2_29_90 String firstPartyString = firstPartyForCookies().string(); if (!firstPartyString.isEmpty()) { GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data())); soup_message_set_first_party(soupMessage, firstParty.get()); } -#endif soup_message_set_flags(soupMessage, m_soupFlags); @@ -104,11 +100,9 @@ void ResourceRequest::updateFromSoupMessage(SoupMessage* soupMessage) if (soupMessage->request_body->data) m_httpBody = FormData::create(soupMessage->request_body->data, soupMessage->request_body->length); -#ifdef HAVE_LIBSOUP_2_29_90 SoupURI* firstParty = soup_message_get_first_party(soupMessage); if (firstParty) m_firstPartyForCookies = soupURIToKURL(firstParty); -#endif m_soupFlags = soup_message_get_flags(soupMessage); diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandle.h b/Source/WebCore/platform/network/soup/SocketStreamHandle.h index 3168fae..c8fe3b3 100644 --- a/Source/WebCore/platform/network/soup/SocketStreamHandle.h +++ b/Source/WebCore/platform/network/soup/SocketStreamHandle.h @@ -60,7 +60,7 @@ namespace WebCore { private: GRefPtr<GSocketConnection> m_socketConnection; GRefPtr<GInputStream> m_inputStream; - GRefPtr<GOutputStream> m_outputStream; + GRefPtr<GPollableOutputStream> m_outputStream; GRefPtr<GSource> m_writeReadySource; char* m_readBuffer; void* m_id; diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp b/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp index 841e209..34382dd 100644 --- a/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp +++ b/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp @@ -50,7 +50,7 @@ namespace WebCore { // These functions immediately call the similarly named SocketStreamHandle methods. static void connectedCallback(GSocketClient*, GAsyncResult*, void*); static void readReadyCallback(GInputStream*, GAsyncResult*, void*); -static gboolean writeReadyCallback(GSocket*, GIOCondition, void*); +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 @@ -82,13 +82,12 @@ SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient : SocketStreamHandleBase(url, client) , m_readBuffer(0) { - // No support for SSL sockets yet. - if (url.protocolIs("wss")) - return; - unsigned int port = url.hasPort() ? url.port() : 80; + 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); } @@ -108,7 +107,7 @@ void SocketStreamHandle::connected(GSocketConnection* socketConnection, GError* } m_socketConnection = adoptGRef(socketConnection); - m_outputStream = g_io_stream_get_output_stream(G_IO_STREAM(m_socketConnection.get())); + 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 = new char[READ_BUFFER_SIZE]; @@ -156,14 +155,14 @@ void SocketStreamHandle::writeReady() int SocketStreamHandle::platformSend(const char* data, int length) { - if (!g_socket_condition_check(g_socket_connection_get_socket(m_socketConnection.get()), G_IO_OUT)) { + if (!g_pollable_output_stream_is_writable(m_outputStream.get())) { beginWaitingForSocketWritability(); return 0; } GOwnPtr<GError> error; - gssize written = g_output_stream_write(m_outputStream.get(), data, length, 0, &error.outPtr()); - if (error) { + gssize written = g_pollable_output_stream_write_nonblocking(m_outputStream.get(), data, length, 0, &error.outPtr()); + if (error && !g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { m_client->didFail(this, SocketStreamError(error->code)); // FIXME: Provide a sensible error. return 0; } @@ -222,8 +221,7 @@ void SocketStreamHandle::beginWaitingForSocketWritability() if (m_writeReadySource) // Already waiting. return; - m_writeReadySource = adoptGRef(g_socket_create_source( - g_socket_connection_get_socket(m_socketConnection.get()), static_cast<GIOCondition>(G_IO_OUT), 0)); + 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); } @@ -266,24 +264,13 @@ static void readReadyCallback(GInputStream* stream, GAsyncResult* result, void* handle->readBytes(bytesRead, error.get()); } -static gboolean writeReadyCallback(GSocket*, GIOCondition condition, void* id) +static gboolean writeReadyCallback(GPollableOutputStream*, void* id) { SocketStreamHandle* handle = getHandleFromId(id); if (!handle) return FALSE; - // G_IO_HUP and G_IO_ERR are are always active. See: - // http://library.gnome.org/devel/gio/stable/GSocket.html#g-socket-create-source - if (condition & G_IO_HUP) { - handle->close(); - return FALSE; - } - if (condition & G_IO_ERR) { - handle->client()->didFail(handle, SocketStreamError(0)); // FIXME: Provide a sensible error. - return FALSE; - } - if (condition & G_IO_OUT) - handle->writeReady(); + handle->writeReady(); return TRUE; } diff --git a/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.c b/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.c deleted file mode 100644 index c14863b..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.c +++ /dev/null @@ -1,200 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2008 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "soup-directory-input-stream.h" - -#include <libsoup/soup.h> -#include <stdio.h> -#include <string.h> - -#define INIT_STRING "<html><head><title>OMG!</title></head><body><table>" -#define EXIT_STRING "</table></html>" - -G_DEFINE_TYPE (WebKitSoupDirectoryInputStream, webkit_soup_directory_input_stream, G_TYPE_INPUT_STREAM) - -static SoupBuffer * -webkit_soup_directory_input_stream_parse_info (WebKitSoupDirectoryInputStream * stream, - GFileInfo * info) -{ - SoupBuffer *buffer; - GString *string; - const char *s; - char *escaped, *path, *xml_string; - - if (!g_file_info_get_name (info)) - return NULL; - - s = g_file_info_get_display_name (info); - if (!s) { - s = g_file_info_get_name (info); - /* FIXME: convert somehow? */ - if (!g_utf8_validate (s, -1, NULL)) - return NULL; - } - string = g_string_new ("<tr>"); - - xml_string = g_markup_escape_text (s, -1); - escaped = g_uri_escape_string (g_file_info_get_name (info), NULL, FALSE); - path = g_strconcat (stream->uri, "/", escaped, NULL); - g_free (escaped); - g_string_append_printf (string, "<td><a href=\"%s\">%s</a></td>", path, xml_string); - g_free (path); - g_free (xml_string); - g_string_append (string, "</tr>"); - - buffer = soup_buffer_new (SOUP_MEMORY_TAKE, string->str, string->len); - g_string_free (string, FALSE); - - return buffer; -} - -static SoupBuffer * -webkit_soup_directory_input_stream_read_next_file (WebKitSoupDirectoryInputStream *stream, - GCancellable *cancellable, - GError **error) -{ - GFileInfo *info; - SoupBuffer *buffer; - GError *err = NULL; - - do { - info = g_file_enumerator_next_file (stream->enumerator, cancellable, &err); - if (info == NULL) { - if (err) { - g_propagate_error (error, err); - return NULL; - } else if (!stream->done) { - stream->done = TRUE; - return soup_buffer_new (SOUP_MEMORY_STATIC, - EXIT_STRING, - sizeof (EXIT_STRING)); - } else { - return NULL; - } - } - - buffer = webkit_soup_directory_input_stream_parse_info (stream, info); - } while (buffer == NULL); - - return buffer; -} - -static gssize -webkit_soup_directory_input_stream_read (GInputStream *input, - void *buffer, - gsize count, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupDirectoryInputStream *stream = WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (input); - gsize total, size; - - for (total = 0; total < count; total += size) { - if (stream->buffer == NULL) { - stream->buffer = webkit_soup_directory_input_stream_read_next_file (stream, cancellable, error); - if (stream->buffer == NULL) { - /* FIXME: Is this correct or should we forward the error? */ - if (total) - g_clear_error (error); - return total; - } - } - - size = MIN (stream->buffer->length, count - total); - memcpy ((char *)buffer + total, stream->buffer->data, size); - if (size == stream->buffer->length) { - soup_buffer_free (stream->buffer); - stream->buffer = NULL; - } else { - SoupBuffer *sub = soup_buffer_new_subbuffer (stream->buffer, - size, - stream->buffer->length - size); - soup_buffer_free (stream->buffer); - stream->buffer = sub; - } - } - - return total; -} - -static gboolean -webkit_soup_directory_input_stream_close (GInputStream *input, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupDirectoryInputStream *stream = WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (input); - gboolean result; - - if (stream->buffer) { - soup_buffer_free (stream->buffer); - stream->buffer = NULL; - } - - result = g_file_enumerator_close (stream->enumerator, - cancellable, - error); - g_object_unref (stream->enumerator); - stream->enumerator = NULL; - - g_free (stream->uri); - stream->uri = NULL; - - return result; -} - -static void -webkit_soup_directory_input_stream_class_init (WebKitSoupDirectoryInputStreamClass *stream_class) -{ - GInputStreamClass *inputstream_class = G_INPUT_STREAM_CLASS (stream_class); - - inputstream_class->read_fn = webkit_soup_directory_input_stream_read; - inputstream_class->close_fn = webkit_soup_directory_input_stream_close; -} - -static void -webkit_soup_directory_input_stream_init (WebKitSoupDirectoryInputStream *stream) -{ - stream->buffer = soup_buffer_new (SOUP_MEMORY_STATIC, - INIT_STRING, - sizeof (INIT_STRING)); -} - -GInputStream * -webkit_soup_directory_input_stream_new (GFileEnumerator *enumerator, - SoupURI *uri) -{ - GInputStream *stream; - - g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL); - g_return_val_if_fail (uri != NULL, NULL); - - stream = g_object_new (WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, NULL); - - WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (stream)->enumerator = g_object_ref (enumerator); - WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (stream)->uri = soup_uri_to_string (uri, FALSE); - - return stream; -} - diff --git a/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.h b/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.h deleted file mode 100644 index 0c5b0be..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2010 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H -#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H 1 - -#include <gio/gio.h> -#include <libsoup/soup-types.h> -#include <libsoup/soup-message-body.h> - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM (webkit_soup_directory_input_stream_get_type ()) -#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStream)) -#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStreamClass)) -#define WEBKIT_IS_SOUP_DIRECTORY_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM)) -#define WEBKIT_IS_SOUP_DIRECTORY_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM)) -#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStreamClass)) - -typedef struct _WebKitSoupDirectoryInputStream WebKitSoupDirectoryInputStream; -typedef struct _WebKitSoupDirectoryInputStreamClass WebKitSoupDirectoryInputStreamClass; - -struct _WebKitSoupDirectoryInputStream { - GInputStream parent; - - GFileEnumerator *enumerator; - char *uri; - SoupBuffer *buffer; - gboolean done; -}; - -struct _WebKitSoupDirectoryInputStreamClass { - GInputStreamClass parent_class; -}; - -GType webkit_soup_directory_input_stream_get_type (void); - -GInputStream *webkit_soup_directory_input_stream_new (GFileEnumerator *enumerator, - SoupURI *uri); - - -G_END_DECLS - -#endif /* WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.c b/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.c deleted file mode 100644 index 2a5d995..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.c +++ /dev/null @@ -1,922 +0,0 @@ -/* soup-input-stream.c, based on gsocketinputstream.c - * - * Copyright (C) 2006-2007 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include <config.h> - -#include <string.h> - -#include <glib.h> -#include <gio/gio.h> - -#include <libsoup/soup.h> - -#include "soup-http-input-stream.h" - -static void webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface); - -G_DEFINE_TYPE_WITH_CODE (WebKitSoupHTTPInputStream, webkit_soup_http_input_stream, G_TYPE_INPUT_STREAM, - G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, - webkit_soup_http_input_stream_seekable_iface_init)) - -typedef void (*WebKitSoupHTTPInputStreamCallback)(GInputStream *); - -typedef struct { - SoupSession *session; - GMainContext *async_context; - SoupMessage *msg; - gboolean got_headers, finished; - goffset offset; - - GCancellable *cancellable; - GSource *cancel_watch; - WebKitSoupHTTPInputStreamCallback got_headers_cb; - WebKitSoupHTTPInputStreamCallback got_chunk_cb; - WebKitSoupHTTPInputStreamCallback finished_cb; - WebKitSoupHTTPInputStreamCallback cancelled_cb; - - guchar *leftover_buffer; - gsize leftover_bufsize, leftover_offset; - - guchar *caller_buffer; - gsize caller_bufsize, caller_nread; - GAsyncReadyCallback outstanding_callback; - GSimpleAsyncResult *result; -} WebKitSoupHTTPInputStreamPrivate; -#define WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamPrivate)) - - -static gssize webkit_soup_http_input_stream_read (GInputStream *stream, - void *buffer, - gsize count, - GCancellable *cancellable, - GError **error); -static gboolean webkit_soup_http_input_stream_close (GInputStream *stream, - GCancellable *cancellable, - GError **error); -static void webkit_soup_http_input_stream_read_async (GInputStream *stream, - void *buffer, - gsize count, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer data); -static gssize webkit_soup_http_input_stream_read_finish (GInputStream *stream, - GAsyncResult *result, - GError **error); -static void webkit_soup_http_input_stream_close_async (GInputStream *stream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer data); -static gboolean webkit_soup_http_input_stream_close_finish (GInputStream *stream, - GAsyncResult *result, - GError **error); - -static goffset webkit_soup_http_input_stream_tell (GSeekable *seekable); - -static gboolean webkit_soup_http_input_stream_can_seek (GSeekable *seekable); -static gboolean webkit_soup_http_input_stream_seek (GSeekable *seekable, - goffset offset, - GSeekType type, - GCancellable *cancellable, - GError **error); - -static gboolean webkit_soup_http_input_stream_can_truncate (GSeekable *seekable); -static gboolean webkit_soup_http_input_stream_truncate (GSeekable *seekable, - goffset offset, - GCancellable *cancellable, - GError **error); - -static void webkit_soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream); -static void webkit_soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer stream); -static void webkit_soup_http_input_stream_finished (SoupMessage *msg, gpointer stream); - -static void -webkit_soup_http_input_stream_finalize (GObject *object) -{ - WebKitSoupHTTPInputStream *stream = WEBKIT_SOUP_HTTP_INPUT_STREAM (object); - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - g_object_unref (priv->session); - - g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_got_headers), stream); - g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_got_chunk), stream); - g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_finished), stream); - g_object_unref (priv->msg); - g_free (priv->leftover_buffer); - - if (G_OBJECT_CLASS (webkit_soup_http_input_stream_parent_class)->finalize) - (*G_OBJECT_CLASS (webkit_soup_http_input_stream_parent_class)->finalize)(object); -} - -static void -webkit_soup_http_input_stream_class_init (WebKitSoupHTTPInputStreamClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); - - g_type_class_add_private (klass, sizeof (WebKitSoupHTTPInputStreamPrivate)); - - gobject_class->finalize = webkit_soup_http_input_stream_finalize; - - stream_class->read_fn = webkit_soup_http_input_stream_read; - stream_class->close_fn = webkit_soup_http_input_stream_close; - stream_class->read_async = webkit_soup_http_input_stream_read_async; - stream_class->read_finish = webkit_soup_http_input_stream_read_finish; - stream_class->close_async = webkit_soup_http_input_stream_close_async; - stream_class->close_finish = webkit_soup_http_input_stream_close_finish; -} - -static void -webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface) -{ - seekable_iface->tell = webkit_soup_http_input_stream_tell; - seekable_iface->can_seek = webkit_soup_http_input_stream_can_seek; - seekable_iface->seek = webkit_soup_http_input_stream_seek; - seekable_iface->can_truncate = webkit_soup_http_input_stream_can_truncate; - seekable_iface->truncate_fn = webkit_soup_http_input_stream_truncate; -} - -static void -webkit_soup_http_input_stream_init (WebKitSoupHTTPInputStream *stream) -{ - ; -} - -static void -webkit_soup_http_input_stream_queue_message (WebKitSoupHTTPInputStream *stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - priv->got_headers = priv->finished = FALSE; - - /* Add an extra ref since soup_session_queue_message steals one */ - g_object_ref (priv->msg); - soup_session_queue_message (priv->session, priv->msg, NULL, NULL); -} - -/** - * webkit_soup_http_input_stream_new: - * @session: the #SoupSession to use - * @msg: the #SoupMessage whose response will be streamed - * - * Prepares to send @msg over @session, and returns a #GInputStream - * that can be used to read the response. - * - * @msg may not be sent until the first read call; if you need to look - * at the status code or response headers before reading the body, you - * can use webkit_soup_http_input_stream_send() or webkit_soup_http_input_stream_send_async() - * to force the message to be sent and the response headers read. - * - * If @msg gets a non-2xx result, the first read (or send) will return - * an error with type %WEBKIT_SOUP_HTTP_INPUT_STREAM_HTTP_ERROR. - * - * Internally, #WebKitSoupHTTPInputStream is implemented using asynchronous I/O, - * so if you are using the synchronous API (eg, - * g_input_stream_read()), you should create a new #GMainContext and - * set it as the %SOUP_SESSION_ASYNC_CONTEXT property on @session. (If - * you don't, then synchronous #GInputStream calls will cause the main - * loop to be run recursively.) The async #GInputStream API works fine - * with %SOUP_SESSION_ASYNC_CONTEXT either set or unset. - * - * Returns: a new #GInputStream. - **/ -WebKitSoupHTTPInputStream * -webkit_soup_http_input_stream_new (SoupSession *session, SoupMessage *msg) -{ - WebKitSoupHTTPInputStream *stream; - WebKitSoupHTTPInputStreamPrivate *priv; - - g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL); - - stream = g_object_new (WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, NULL); - priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - priv->session = g_object_ref (session); - priv->async_context = soup_session_get_async_context (session); - priv->msg = g_object_ref (msg); - - g_signal_connect (msg, "got_headers", - G_CALLBACK (webkit_soup_http_input_stream_got_headers), stream); - g_signal_connect (msg, "got_chunk", - G_CALLBACK (webkit_soup_http_input_stream_got_chunk), stream); - g_signal_connect (msg, "finished", - G_CALLBACK (webkit_soup_http_input_stream_finished), stream); - - webkit_soup_http_input_stream_queue_message (stream); - return stream; -} - -static void -webkit_soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - /* If the status is unsuccessful, we just ignore the signal and let - * libsoup keep going (eventually either it will requeue the request - * (after handling authentication/redirection), or else the - * "finished" handler will run). - */ - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) - return; - - priv->got_headers = TRUE; - if (!priv->caller_buffer) { - /* Not ready to read the body yet */ - soup_session_pause_message (priv->session, msg); - } - - if (priv->got_headers_cb) - priv->got_headers_cb (stream); -} - -static void -webkit_soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk_buffer, - gpointer stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - const gchar *chunk = chunk_buffer->data; - gsize chunk_size = chunk_buffer->length; - - /* We only pay attention to the chunk if it's part of a successful - * response. - */ - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) - return; - - /* Sanity check */ - if (priv->caller_bufsize == 0 || priv->leftover_bufsize != 0) - g_warning ("webkit_soup_http_input_stream_got_chunk called again before previous chunk was processed"); - - /* Copy what we can into priv->caller_buffer */ - if (priv->caller_bufsize > priv->caller_nread) { - gsize nread = MIN (chunk_size, priv->caller_bufsize - priv->caller_nread); - - memcpy (priv->caller_buffer + priv->caller_nread, chunk, nread); - priv->caller_nread += nread; - priv->offset += nread; - chunk += nread; - chunk_size -= nread; - } - - if (chunk_size > 0) { - /* Copy the rest into priv->leftover_buffer. If - * there's already some data there, realloc and - * append. Otherwise just copy. - */ - if (priv->leftover_bufsize) { - priv->leftover_buffer = g_realloc (priv->leftover_buffer, - priv->leftover_bufsize + chunk_size); - memcpy (priv->leftover_buffer + priv->leftover_bufsize, - chunk, chunk_size); - priv->leftover_bufsize += chunk_size; - } else { - priv->leftover_bufsize = chunk_size; - priv->leftover_buffer = g_memdup (chunk, chunk_size); - priv->leftover_offset = 0; - } - } - - soup_session_pause_message (priv->session, msg); - if (priv->got_chunk_cb) - priv->got_chunk_cb (stream); -} - -static void -webkit_soup_http_input_stream_finished (SoupMessage *msg, gpointer stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - priv->finished = TRUE; - - if (priv->finished_cb) - priv->finished_cb (stream); -} - -static gboolean -webkit_soup_http_input_stream_cancelled (GIOChannel *chan, GIOCondition condition, - gpointer stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - priv->cancel_watch = NULL; - - soup_session_pause_message (priv->session, priv->msg); - if (priv->cancelled_cb) - priv->cancelled_cb (stream); - - return FALSE; -} - -static void -webkit_soup_http_input_stream_prepare_for_io (GInputStream *stream, - GCancellable *cancellable, - guchar *buffer, - gsize count) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - int cancel_fd; - - priv->cancellable = cancellable; - cancel_fd = g_cancellable_get_fd (cancellable); - if (cancel_fd != -1) { - GIOChannel *chan = g_io_channel_unix_new (cancel_fd); - priv->cancel_watch = soup_add_io_watch (priv->async_context, chan, - G_IO_IN | G_IO_ERR | G_IO_HUP, - webkit_soup_http_input_stream_cancelled, - stream); - g_io_channel_unref (chan); - } - - priv->caller_buffer = buffer; - priv->caller_bufsize = count; - priv->caller_nread = 0; - - if (priv->got_headers) - soup_session_unpause_message (priv->session, priv->msg); -} - -static void -webkit_soup_http_input_stream_done_io (GInputStream *stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - if (priv->cancel_watch) { - g_source_destroy (priv->cancel_watch); - priv->cancel_watch = NULL; - g_cancellable_release_fd (priv->cancellable); - } - priv->cancellable = NULL; - - priv->caller_buffer = NULL; - priv->caller_bufsize = 0; -} - -static gboolean -set_error_if_http_failed (SoupMessage *msg, GError **error) -{ - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - g_set_error_literal (error, SOUP_HTTP_ERROR, - msg->status_code, msg->reason_phrase); - return TRUE; - } - return FALSE; -} - -static gsize -read_from_leftover (WebKitSoupHTTPInputStreamPrivate *priv, - gpointer buffer, gsize bufsize) -{ - gsize nread; - - if (priv->leftover_bufsize - priv->leftover_offset <= bufsize) { - nread = priv->leftover_bufsize - priv->leftover_offset; - memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread); - - g_free (priv->leftover_buffer); - priv->leftover_buffer = NULL; - priv->leftover_bufsize = priv->leftover_offset = 0; - } else { - nread = bufsize; - memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread); - priv->leftover_offset += nread; - } - - priv->offset += nread; - return nread; -} - -/* This does the work of webkit_soup_http_input_stream_send(), assuming that the - * GInputStream pending flag has already been set. It is also used by - * webkit_soup_http_input_stream_send_async() in some circumstances. - */ -static gboolean -webkit_soup_http_input_stream_send_internal (GInputStream *stream, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0); - while (!priv->finished && !priv->got_headers && - !g_cancellable_is_cancelled (cancellable)) - g_main_context_iteration (priv->async_context, TRUE); - webkit_soup_http_input_stream_done_io (stream); - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - else if (set_error_if_http_failed (priv->msg, error)) - return FALSE; - return TRUE; -} - -static void -send_sync_finished (GInputStream *stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - GError *error = NULL; - - if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error)) - set_error_if_http_failed (priv->msg, &error); - - priv->got_headers_cb = NULL; - priv->finished_cb = NULL; - - /* Wake up the main context iteration */ - g_source_attach (g_idle_source_new (), NULL); -} - -/** - * webkit_soup_http_input_stream_send: - * @httpstream: a #WebKitSoupHTTPInputStream - * @cancellable: optional #GCancellable object, %NULL to ignore. - * @error: location to store the error occuring, or %NULL to ignore - * - * Synchronously sends the HTTP request associated with @stream, and - * reads the response headers. Call this after webkit_soup_http_input_stream_new() - * and before the first g_input_stream_read() if you want to check the - * HTTP status code before you start reading. - * - * Return value: %TRUE if msg has a successful (2xx) status, %FALSE if - * not. - **/ -gboolean -webkit_soup_http_input_stream_send (WebKitSoupHTTPInputStream *httpstream, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream); - GInputStream *istream = (GInputStream *)httpstream; - gboolean result; - - g_return_val_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream), FALSE); - - if (!g_input_stream_set_pending (istream, error)) - return FALSE; - - priv->got_headers_cb = send_sync_finished; - priv->finished_cb = send_sync_finished; - - result = webkit_soup_http_input_stream_send_internal (istream, cancellable, error); - g_input_stream_clear_pending (istream); - - return result; -} - -static gssize -webkit_soup_http_input_stream_read (GInputStream *stream, - void *buffer, - gsize count, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - if (priv->finished) - return 0; - - /* If there is data leftover from a previous read, return it. */ - if (priv->leftover_bufsize) - return read_from_leftover (priv, buffer, count); - - /* No leftover data, accept one chunk from the network */ - webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count); - while (!priv->finished && priv->caller_nread == 0 && - !g_cancellable_is_cancelled (cancellable)) - g_main_context_iteration (priv->async_context, TRUE); - webkit_soup_http_input_stream_done_io (stream); - - if (priv->caller_nread > 0) - return priv->caller_nread; - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return -1; - else if (set_error_if_http_failed (priv->msg, error)) - return -1; - else - return 0; -} - -static gboolean -webkit_soup_http_input_stream_close (GInputStream *stream, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - if (!priv->finished) - soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED); - - return TRUE; -} - -static void -wrapper_callback (GObject *source_object, GAsyncResult *res, - gpointer user_data) -{ - GInputStream *stream = G_INPUT_STREAM (source_object); - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - g_input_stream_clear_pending (stream); - if (priv->outstanding_callback) - (*priv->outstanding_callback)(source_object, res, user_data); - priv->outstanding_callback = NULL; - g_object_unref (stream); -} - -static void -send_async_thread (GSimpleAsyncResult *res, - GObject *object, - GCancellable *cancellable) -{ - GError *error = NULL; - gboolean success; - - success = webkit_soup_http_input_stream_send_internal (G_INPUT_STREAM (object), - cancellable, &error); - g_simple_async_result_set_op_res_gboolean (res, success); - if (error) { - g_simple_async_result_set_from_error (res, error); - g_error_free (error); - } -} - -static void -webkit_soup_http_input_stream_send_async_in_thread (GInputStream *stream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *res; - - res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, - webkit_soup_http_input_stream_send_async_in_thread); - g_simple_async_result_run_in_thread (res, send_async_thread, - io_priority, cancellable); - g_object_unref (res); -} - -static void -send_async_finished (GInputStream *stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - GSimpleAsyncResult *result; - GError *error = NULL; - - if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error)) - set_error_if_http_failed (priv->msg, &error); - - priv->got_headers_cb = NULL; - priv->finished_cb = NULL; - webkit_soup_http_input_stream_done_io (stream); - - result = priv->result; - priv->result = NULL; - - g_simple_async_result_set_op_res_gboolean (result, error == NULL); - if (error) { - g_simple_async_result_set_from_error (result, error); - g_error_free (error); - } - g_simple_async_result_complete (result); - g_object_unref (result); -} - -static void -webkit_soup_http_input_stream_send_async_internal (GInputStream *stream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - g_object_ref (stream); - priv->outstanding_callback = callback; - - /* If the session uses the default GMainContext, then we can do - * async I/O directly. But if it has its own main context, it's - * easier to just run it in another thread. - */ - if (soup_session_get_async_context (priv->session)) { - webkit_soup_http_input_stream_send_async_in_thread (stream, io_priority, cancellable, - wrapper_callback, user_data); - return; - } - - priv->got_headers_cb = send_async_finished; - priv->finished_cb = send_async_finished; - - webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0); - priv->result = g_simple_async_result_new (G_OBJECT (stream), - wrapper_callback, user_data, - webkit_soup_http_input_stream_send_async); -} - -/** - * webkit_soup_http_input_stream_send_async: - * @httpstream: a #WebKitSoupHTTPInputStream - * @io_priority: the io priority of the request. - * @cancellable: optional #GCancellable object, %NULL to ignore. - * @callback: callback to call when the request is satisfied - * @user_data: the data to pass to callback function - * - * Asynchronously sends the HTTP request associated with @stream, and - * reads the response headers. Call this after webkit_soup_http_input_stream_new() - * and before the first g_input_stream_read_async() if you want to - * check the HTTP status code before you start reading. - **/ -void -webkit_soup_http_input_stream_send_async (WebKitSoupHTTPInputStream *httpstream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GInputStream *istream = (GInputStream *)httpstream; - GError *error = NULL; - - g_return_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream)); - - if (!g_input_stream_set_pending (istream, &error)) { - g_simple_async_report_gerror_in_idle (G_OBJECT (httpstream), - callback, - user_data, - error); - g_error_free (error); - return; - } - webkit_soup_http_input_stream_send_async_internal (istream, io_priority, cancellable, - callback, user_data); -} - -/** - * webkit_soup_http_input_stream_send_finish: - * @httpstream: a #WebKitSoupHTTPInputStream - * @result: a #GAsyncResult. - * @error: a #GError location to store the error occuring, or %NULL to - * ignore. - * - * Finishes a webkit_soup_http_input_stream_send_async() operation. - * - * Return value: %TRUE if the message was sent successfully and - * received a successful status code, %FALSE if not. - **/ -gboolean -webkit_soup_http_input_stream_send_finish (WebKitSoupHTTPInputStream *httpstream, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); - simple = G_SIMPLE_ASYNC_RESULT (result); - - g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_http_input_stream_send_async, FALSE); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return g_simple_async_result_get_op_res_gboolean (simple); -} - -static void -read_async_done (GInputStream *stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - GSimpleAsyncResult *result; - GError *error = NULL; - - result = priv->result; - priv->result = NULL; - - if (g_cancellable_set_error_if_cancelled (priv->cancellable, &error) || - set_error_if_http_failed (priv->msg, &error)) { - g_simple_async_result_set_from_error (result, error); - g_error_free (error); - } else - g_simple_async_result_set_op_res_gssize (result, priv->caller_nread); - - priv->got_chunk_cb = NULL; - priv->finished_cb = NULL; - priv->cancelled_cb = NULL; - webkit_soup_http_input_stream_done_io (stream); - - g_simple_async_result_complete (result); - g_object_unref (result); -} - -static void -webkit_soup_http_input_stream_read_async (GInputStream *stream, - void *buffer, - gsize count, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - GSimpleAsyncResult *result; - - /* If the session uses the default GMainContext, then we can do - * async I/O directly. But if it has its own main context, we fall - * back to the async-via-sync-in-another-thread implementation. - */ - if (soup_session_get_async_context (priv->session)) { - G_INPUT_STREAM_CLASS (webkit_soup_http_input_stream_parent_class)-> - read_async (stream, buffer, count, io_priority, - cancellable, callback, user_data); - return; - } - - result = g_simple_async_result_new (G_OBJECT (stream), - callback, user_data, - webkit_soup_http_input_stream_read_async); - - if (priv->finished) { - g_simple_async_result_set_op_res_gssize (result, 0); - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); - return; - } - - if (priv->leftover_bufsize) { - gsize nread = read_from_leftover (priv, buffer, count); - g_simple_async_result_set_op_res_gssize (result, nread); - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); - return; - } - - priv->result = result; - - priv->got_chunk_cb = read_async_done; - priv->finished_cb = read_async_done; - priv->cancelled_cb = read_async_done; - webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count); -} - -static gssize -webkit_soup_http_input_stream_read_finish (GInputStream *stream, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), -1); - simple = G_SIMPLE_ASYNC_RESULT (result); - g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_http_input_stream_read_async, -1); - - return g_simple_async_result_get_op_res_gssize (simple); -} - -static void -webkit_soup_http_input_stream_close_async (GInputStream *stream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *result; - gboolean success; - GError *error = NULL; - - result = g_simple_async_result_new (G_OBJECT (stream), - callback, user_data, - webkit_soup_http_input_stream_close_async); - success = webkit_soup_http_input_stream_close (stream, cancellable, &error); - g_simple_async_result_set_op_res_gboolean (result, success); - if (error) { - g_simple_async_result_set_from_error (result, error); - g_error_free (error); - } - - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); -} - -static gboolean -webkit_soup_http_input_stream_close_finish (GInputStream *stream, - GAsyncResult *result, - GError **error) -{ - /* Failures handled in generic close_finish code */ - return TRUE; -} - -static goffset -webkit_soup_http_input_stream_tell (GSeekable *seekable) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable); - - return priv->offset; -} - -static gboolean -webkit_soup_http_input_stream_can_seek (GSeekable *seekable) -{ - return TRUE; -} - -extern void soup_message_io_cleanup (SoupMessage *msg); - -static gboolean -webkit_soup_http_input_stream_seek (GSeekable *seekable, - goffset offset, - GSeekType type, - GCancellable *cancellable, - GError **error) -{ - GInputStream *stream = G_INPUT_STREAM (seekable); - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable); - char *range; - - if (type == G_SEEK_END) { - /* FIXME: we could send "bytes=-offset", but unless we - * know the Content-Length, we wouldn't be able to - * answer a tell() properly. We could find the - * Content-Length by doing a HEAD... - */ - - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "G_SEEK_END not currently supported"); - return FALSE; - } - - if (!g_input_stream_set_pending (stream, error)) - return FALSE; - - soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED); - soup_message_io_cleanup (priv->msg); - - switch (type) { - case G_SEEK_CUR: - offset += priv->offset; - /* fall through */ - - case G_SEEK_SET: - range = g_strdup_printf ("bytes=%" G_GUINT64_FORMAT "-", (guint64)offset); - priv->offset = offset; - break; - - case G_SEEK_END: - range = NULL; /* keep compilers happy */ - g_return_val_if_reached (FALSE); - break; - - default: - g_return_val_if_reached (FALSE); - } - - soup_message_headers_remove (priv->msg->request_headers, "Range"); - soup_message_headers_append (priv->msg->request_headers, "Range", range); - g_free (range); - - webkit_soup_http_input_stream_queue_message (WEBKIT_SOUP_HTTP_INPUT_STREAM (stream)); - - g_input_stream_clear_pending (stream); - return TRUE; -} - -static gboolean -webkit_soup_http_input_stream_can_truncate (GSeekable *seekable) -{ - return FALSE; -} - -static gboolean -webkit_soup_http_input_stream_truncate (GSeekable *seekable, - goffset offset, - GCancellable *cancellable, - GError **error) -{ - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "Truncate not allowed on input stream"); - return FALSE; -} - -SoupMessage * -webkit_soup_http_input_stream_get_message (WebKitSoupHTTPInputStream *httpstream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream); - return priv->msg ? g_object_ref (priv->msg) : NULL; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.h b/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.h deleted file mode 100644 index 6b98559..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2006, 2007, 2009 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__ -#define __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__ - -#include <gio/gio.h> -#include <libsoup/soup-types.h> - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM (webkit_soup_http_input_stream_get_type ()) -#define WEBKIT_SOUP_HTTP_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStream)) -#define WEBKIT_SOUP_HTTP_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamClass)) -#define WEBKIT_IS_SOUP_HTTP_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM)) -#define WEBKIT_IS_SOUP_HTTP_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM)) -#define WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamClass)) - -typedef struct WebKitSoupHTTPInputStream WebKitSoupHTTPInputStream; -typedef struct WebKitSoupHTTPInputStreamClass WebKitSoupHTTPInputStreamClass; - -struct WebKitSoupHTTPInputStream { - GInputStream parent; -}; - -struct WebKitSoupHTTPInputStreamClass { - GInputStreamClass parent_class; - - /* Padding for future expansion */ - void (*_g_reserved1)(void); - void (*_g_reserved2)(void); - void (*_g_reserved3)(void); - void (*_g_reserved4)(void); - void (*_g_reserved5)(void); -}; - -GType webkit_soup_http_input_stream_get_type (void) G_GNUC_CONST; - -WebKitSoupHTTPInputStream *webkit_soup_http_input_stream_new (SoupSession *session, - SoupMessage *msg); - -gboolean webkit_soup_http_input_stream_send (WebKitSoupHTTPInputStream *httpstream, - GCancellable *cancellable, - GError **error); - -void webkit_soup_http_input_stream_send_async (WebKitSoupHTTPInputStream *httpstream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean webkit_soup_http_input_stream_send_finish (WebKitSoupHTTPInputStream *httpstream, - GAsyncResult *result, - GError **error); - -SoupMessage *webkit_soup_http_input_stream_get_message (WebKitSoupHTTPInputStream *httpstream); - -G_END_DECLS - -#endif /* __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__ */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-data.c b/Source/WebCore/platform/network/soup/cache/soup-request-data.c deleted file mode 100644 index ced5c4a..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-data.c +++ /dev/null @@ -1,170 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-request-data.c: data: URI request object - * - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif -#include "soup-request-data.h" - -#include "soup-requester.h" -#include <libsoup/soup.h> -#include <glib/gi18n.h> - -G_DEFINE_TYPE (WebKitSoupRequestData, webkit_soup_request_data, WEBKIT_TYPE_SOUP_REQUEST) - -struct _WebKitSoupRequestDataPrivate { - gsize content_length; - char *content_type; -}; - -static void -webkit_soup_request_data_init (WebKitSoupRequestData *data) -{ - data->priv = G_TYPE_INSTANCE_GET_PRIVATE (data, WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataPrivate); -} - -static void -webkit_soup_request_data_finalize (GObject *object) -{ - WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (object); - - g_free (data->priv->content_type); - - G_OBJECT_CLASS (webkit_soup_request_data_parent_class)->finalize (object); -} - -static gboolean -webkit_soup_request_data_check_uri (WebKitSoupRequest *request, - SoupURI *uri, - GError **error) -{ - return uri->host == NULL; -} - -static GInputStream * -webkit_soup_request_data_send (WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request); - SoupURI *uri = webkit_soup_request_get_uri (request); - GInputStream *memstream; - const char *comma, *semi, *start, *end; - gboolean base64 = FALSE; - - gchar *uristr = soup_uri_to_string (uri, FALSE); - comma = strchr (uristr, ','); - if (comma && comma != uristr) { - /* Deal with MIME type / params */ - semi = memchr (uristr, ';', comma - uristr); - end = semi ? semi : comma; - - if (semi && !g_ascii_strncasecmp (semi, ";base64", MAX ((size_t) (comma - semi), strlen (";base64")))) - base64 = TRUE; - - if (end != uristr) - if (base64) - data->priv->content_type = g_strndup (uristr, end - uristr); - else - data->priv->content_type = - webkit_soup_request_uri_decoded_copy (uristr, end - uristr); - } - - memstream = g_memory_input_stream_new (); - - start = comma ? comma + 1 : uristr; - - if (*start) { - guchar *buf; - - if (base64) { - int inlen, state = 0; - guint save = 0; - - inlen = strlen (start); - buf = g_malloc0 (inlen * 3 / 4 + 3); - data->priv->content_length = - g_base64_decode_step (start, inlen, buf, - &state, &save); - if (state != 0) { - g_free (buf); - goto fail; - } - } else { - /* Cannot use g_uri_unescape_string nor - soup_uri_decode because we don't want to - fail for things like "%3E%%3C" -> ">%<" */ - buf = (guchar *)webkit_soup_request_uri_decoded_copy (start, strlen (start)); - if (!buf) - goto fail; - data->priv->content_length = strlen ((char *)buf); - } - - g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (memstream), - buf, data->priv->content_length, - g_free); - } - g_free (uristr); - - return memstream; - - fail: - g_free (uristr); - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI, - _ ("Unable to decode URI: %s"), start); - g_object_unref (memstream); - return NULL; -} - -static goffset -webkit_soup_request_data_get_content_length (WebKitSoupRequest *request) -{ - WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request); - - return data->priv->content_length; -} - -static const char * -webkit_soup_request_data_get_content_type (WebKitSoupRequest *request) -{ - WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request); - - return data->priv->content_type; -} - -static void -webkit_soup_request_data_class_init (WebKitSoupRequestDataClass *request_data_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (request_data_class); - WebKitSoupRequestClass *request_class = - WEBKIT_SOUP_REQUEST_CLASS (request_data_class); - - g_type_class_add_private (request_data_class, sizeof (WebKitSoupRequestDataPrivate)); - - object_class->finalize = webkit_soup_request_data_finalize; - - request_class->check_uri = webkit_soup_request_data_check_uri; - request_class->send = webkit_soup_request_data_send; - request_class->get_content_length = webkit_soup_request_data_get_content_length; - request_class->get_content_type = webkit_soup_request_data_get_content_type; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-data.h b/Source/WebCore/platform/network/soup/cache/soup-request-data.h deleted file mode 100644 index c9631a4..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-data.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_REQUEST_DATA_H -#define WEBKIT_SOUP_REQUEST_DATA_H 1 - -#include "soup-request.h" - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_REQUEST_DATA (webkit_soup_request_data_get_type ()) -#define WEBKIT_SOUP_REQUEST_DATA(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestData)) -#define WEBKIT_SOUP_REQUEST_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataClass)) -#define WEBKIT_IS_SOUP_REQUEST_DATA(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_DATA)) -#define WEBKIT_IS_SOUP_REQUEST_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_DATA)) -#define WEBKIT_SOUP_REQUEST_DATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataClass)) - -typedef struct _WebKitSoupRequestDataPrivate WebKitSoupRequestDataPrivate; - -typedef struct { - WebKitSoupRequest parent; - - WebKitSoupRequestDataPrivate *priv; -} WebKitSoupRequestData; - -typedef struct { - WebKitSoupRequestClass parent; -} WebKitSoupRequestDataClass; - -GType webkit_soup_request_data_get_type (void); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_REQUEST_DATA_H */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-file.c b/Source/WebCore/platform/network/soup/cache/soup-request-file.c deleted file mode 100644 index 24ccb10..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-file.c +++ /dev/null @@ -1,331 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-request-file.c: file: URI request object - * - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "soup-request-file.h" -#include "soup-directory-input-stream.h" -#include "soup-requester.h" -#include <glib/gi18n.h> - -G_DEFINE_TYPE (WebKitSoupRequestFile, webkit_soup_request_file, WEBKIT_TYPE_SOUP_REQUEST) - -struct _WebKitSoupRequestFilePrivate { - GFile *gfile; - - char *mime_type; - goffset size; -}; - -GFile * -webkit_soup_request_file_get_file (WebKitSoupRequestFile *file) -{ - return g_object_ref (file->priv->gfile); -} - -static void -webkit_soup_request_file_init (WebKitSoupRequestFile *file) -{ - file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFilePrivate); - - file->priv->size = -1; -} - -static void -webkit_soup_request_file_finalize (GObject *object) -{ - WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (object); - - if (file->priv->gfile) - g_object_unref (file->priv->gfile); - g_free (file->priv->mime_type); - - G_OBJECT_CLASS (webkit_soup_request_file_parent_class)->finalize (object); -} - -static gboolean -webkit_soup_request_file_check_uri (WebKitSoupRequest *request, - SoupURI *uri, - GError **error) -{ - /* "file:/foo" is not valid */ - if (!uri->host) - return FALSE; - - /* but it must be "file:///..." or "file://localhost/..." */ - if (uri->scheme == SOUP_URI_SCHEME_FILE && - *uri->host && - g_ascii_strcasecmp (uri->host, "localhost") != 0) - return FALSE; - - return TRUE; -} - -static void -webkit_soup_request_file_ftp_main_loop_quit (GObject *object, - GAsyncResult *result, - gpointer loop) -{ - g_main_loop_quit (loop); -} - -/* This is a somewhat hacky way to get FTP to almost work. The proper way to - * get FTP to _really_ work involves hacking GIO to have APIs to handle - * canoncial URLs. - */ -static GFile * -webkit_soup_request_file_ensure_file_ftp (SoupURI *uri, - GCancellable *cancellable, - GError **error) -{ - SoupURI *host; - char *s; - GFile *file, *result; - GMount *mount; - - host = soup_uri_copy_host (uri); - s = soup_uri_to_string (host, FALSE); - file = g_file_new_for_uri (s); - soup_uri_free (host); - g_free (s); - - mount = g_file_find_enclosing_mount (file, cancellable, error); - if (mount == NULL && g_file_supports_thread_contexts (file)) { - GMainContext *context = g_main_context_new (); - GMainLoop *loop = g_main_loop_new (context, FALSE); - - g_clear_error (error); - g_main_context_push_thread_default (context); - g_file_mount_enclosing_volume (file, - G_MOUNT_MOUNT_NONE, - NULL, /* FIXME! */ - cancellable, - webkit_soup_request_file_ftp_main_loop_quit, - loop); - g_main_loop_run (loop); - g_main_context_pop_thread_default (context); - g_main_loop_unref (loop); - g_main_context_unref (context); - mount = g_file_find_enclosing_mount (file, cancellable, error); - } - if (mount == NULL) - return NULL; - g_object_unref (file); - - file = g_mount_get_default_location (mount); - g_object_unref (mount); - - s = g_strdup (uri->path); - if (strchr (s, ';')) - *strchr (s, ';') = 0; - - result = g_file_resolve_relative_path (file, s); - g_free (s); - g_object_unref (file); - - return result; -} - -static gboolean -webkit_soup_request_file_ensure_file (WebKitSoupRequestFile *file, - GCancellable *cancellable, - GError **error) -{ - SoupURI *uri; - - if (file->priv->gfile) - return TRUE; - - uri = webkit_soup_request_get_uri (WEBKIT_SOUP_REQUEST (file)); - if (uri->scheme == SOUP_URI_SCHEME_FILE) { - /* We cannot use soup_uri_decode as it incorrectly - * returns NULL for incorrectly encoded URIs (that - * could be valid filenames). This will be hopefully - * shipped in libsoup 2.32.1 but we want to land this - * first. TODO: replace uri_decoded_copy by - * soup_uri_decode when the required libsoup version - * is bumped out to 2.32.1 - */ - gchar *decoded_uri = webkit_soup_request_uri_decoded_copy (uri->path, strlen (uri->path)); - - if (decoded_uri) { - /* Do not use new_for_uri() as the decoded URI - * could not be a valid URI - */ - file->priv->gfile = g_file_new_for_path (decoded_uri); - g_free (decoded_uri); - } - - return TRUE; - } else if (uri->scheme == SOUP_URI_SCHEME_FTP) { - file->priv->gfile = webkit_soup_request_file_ensure_file_ftp (uri, - cancellable, - error); - return file->priv->gfile != NULL; - } - - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME, - _ ("Unsupported URI scheme '%s'"), uri->scheme); - return FALSE; -} - -static GInputStream * -webkit_soup_request_file_send (WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request); - GInputStream *stream; - GError *my_error = NULL; - - if (!webkit_soup_request_file_ensure_file (file, cancellable, error)) - return NULL; - - stream = G_INPUT_STREAM (g_file_read (file->priv->gfile, - cancellable, &my_error)); - if (stream == NULL) { - if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) { - GFileEnumerator *enumerator; - g_clear_error (&my_error); - enumerator = g_file_enumerate_children (file->priv->gfile, - "*", - G_FILE_QUERY_INFO_NONE, - cancellable, - error); - if (enumerator) { - stream = webkit_soup_directory_input_stream_new (enumerator, - webkit_soup_request_get_uri (request)); - g_object_unref (enumerator); - file->priv->mime_type = g_strdup ("text/html"); - } - } else { - g_propagate_error (error, my_error); - } - } else { - GFileInfo *info = g_file_query_info (file->priv->gfile, - G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," - G_FILE_ATTRIBUTE_STANDARD_SIZE, - 0, cancellable, NULL); - if (info) { - const char *content_type; - file->priv->size = g_file_info_get_size (info); - content_type = g_file_info_get_content_type (info); - - if (content_type) - file->priv->mime_type = g_content_type_get_mime_type (content_type); - g_object_unref (info); - } - } - - return stream; -} - -static void -webkit_soup_request_file_send_async_thread (GSimpleAsyncResult *res, - GObject *object, - GCancellable *cancellable) -{ - GInputStream *stream; - WebKitSoupRequest *request; - GError *error = NULL; - - request = WEBKIT_SOUP_REQUEST (object); - - stream = webkit_soup_request_file_send (request, cancellable, &error); - - if (stream == NULL) { - g_simple_async_result_set_from_error (res, error); - g_error_free (error); - } else { - g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref); - } -} - -static void -webkit_soup_request_file_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *res; - - res = g_simple_async_result_new (G_OBJECT (request), callback, user_data, webkit_soup_request_file_send_async); - - g_simple_async_result_run_in_thread (res, webkit_soup_request_file_send_async_thread, G_PRIORITY_DEFAULT, cancellable); - g_object_unref (res); -} - -static GInputStream * -webkit_soup_request_file_send_finish (WebKitSoupRequest *request, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); - - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_request_file_send_async); - - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - - return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); -} - -static goffset -webkit_soup_request_file_get_content_length (WebKitSoupRequest *request) -{ - WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request); - - return file->priv->size; -} - -static const char * -webkit_soup_request_file_get_content_type (WebKitSoupRequest *request) -{ - WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request); - - if (!file->priv->mime_type) - return "application/octet-stream"; - - return file->priv->mime_type; -} - -static void -webkit_soup_request_file_class_init (WebKitSoupRequestFileClass *request_file_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (request_file_class); - WebKitSoupRequestClass *request_class = - WEBKIT_SOUP_REQUEST_CLASS (request_file_class); - - g_type_class_add_private (request_file_class, sizeof (WebKitSoupRequestFilePrivate)); - - object_class->finalize = webkit_soup_request_file_finalize; - - request_class->check_uri = webkit_soup_request_file_check_uri; - request_class->send = webkit_soup_request_file_send; - request_class->send_async = webkit_soup_request_file_send_async; - request_class->send_finish = webkit_soup_request_file_send_finish; - request_class->get_content_length = webkit_soup_request_file_get_content_length; - request_class->get_content_type = webkit_soup_request_file_get_content_type; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-file.h b/Source/WebCore/platform/network/soup/cache/soup-request-file.h deleted file mode 100644 index 459e82a..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-file.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_REQUEST_FILE_H -#define WEBKIT_SOUP_REQUEST_FILE_H 1 - -#include "soup-request.h" - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_REQUEST_FILE (webkit_soup_request_file_get_type ()) -#define WEBKIT_SOUP_REQUEST_FILE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFile)) -#define WEBKIT_SOUP_REQUEST_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFileClass)) -#define WEBKIT_IS_SOUP_REQUEST_FILE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_FILE)) -#define WEBKIT_IS_SOUP_REQUEST_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_FILE)) -#define WEBKIT_SOUP_REQUEST_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFileClass)) - -typedef struct _WebKitSoupRequestFilePrivate WebKitSoupRequestFilePrivate; - -typedef struct { - WebKitSoupRequest parent; - - WebKitSoupRequestFilePrivate *priv; -} WebKitSoupRequestFile; - -typedef struct { - WebKitSoupRequestClass parent; -} WebKitSoupRequestFileClass; - -GType webkit_soup_request_file_get_type (void); - -GFile *webkit_soup_request_file_get_file (WebKitSoupRequestFile *file); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_REQUEST_FILE_H */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-http.c b/Source/WebCore/platform/network/soup/cache/soup-request-http.c deleted file mode 100644 index 777fd72..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-http.c +++ /dev/null @@ -1,356 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-request-http.c: http: URI request object - * - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <glib/gi18n.h> - -#include "soup-cache.h" -#include "soup-cache-private.h" -#include "soup-http-input-stream.h" -#include "soup-request-http.h" - -G_DEFINE_TYPE (WebKitSoupRequestHTTP, webkit_soup_request_http, WEBKIT_TYPE_SOUP_REQUEST) - -struct _WebKitSoupRequestHTTPPrivate { - SoupMessage *msg; -}; - -/** - * webkit_soup_request_http_get_message: - * @http: a #WebKitSoupRequestHTTP object - * - * Gets a new reference to the #SoupMessage associated to this SoupRequest - * - * Returns: a new reference to the #SoupMessage - **/ -SoupMessage * -webkit_soup_request_http_get_message (WebKitSoupRequestHTTP *http) -{ - g_return_val_if_fail (WEBKIT_IS_SOUP_REQUEST_HTTP (http), NULL); - - return g_object_ref (http->priv->msg); -} - -static void -webkit_soup_request_http_init (WebKitSoupRequestHTTP *http) -{ - http->priv = G_TYPE_INSTANCE_GET_PRIVATE (http, WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPPrivate); -} - -static gboolean -webkit_soup_request_http_check_uri (WebKitSoupRequest *request, - SoupURI *uri, - GError **error) -{ - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request); - - if (!SOUP_URI_VALID_FOR_HTTP (uri)) - return FALSE; - - http->priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); - return TRUE; -} - -static void -webkit_soup_request_http_finalize (GObject *object) -{ - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (object); - - if (http->priv->msg) - g_object_unref (http->priv->msg); - - G_OBJECT_CLASS (webkit_soup_request_http_parent_class)->finalize (object); -} - -static GInputStream * -webkit_soup_request_http_send (WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupHTTPInputStream *httpstream; - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request); - - httpstream = webkit_soup_http_input_stream_new (webkit_soup_request_get_session (request), http->priv->msg); - if (!webkit_soup_http_input_stream_send (httpstream, cancellable, error)) { - g_object_unref (httpstream); - return NULL; - } - return (GInputStream *)httpstream; -} - - -static void -sent_async (GObject *source, GAsyncResult *result, gpointer user_data) -{ - WebKitSoupHTTPInputStream *httpstream = WEBKIT_SOUP_HTTP_INPUT_STREAM (source); - GSimpleAsyncResult *simple = user_data; - GError *error = NULL; - - if (webkit_soup_http_input_stream_send_finish (httpstream, result, &error)) { - g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref); - } else { - g_simple_async_result_set_from_error (simple, error); - g_error_free (error); - g_object_unref (httpstream); - } - g_simple_async_result_complete (simple); - g_object_unref (simple); -} - - -typedef struct { - WebKitSoupRequestHTTP *req; - SoupMessage *original; - GCancellable *cancellable; - GAsyncReadyCallback callback; - gpointer user_data; -} ConditionalHelper; - - -static void -conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) -{ - ConditionalHelper *helper = (ConditionalHelper *)user_data; - GSimpleAsyncResult *simple; - WebKitSoupHTTPInputStream *httpstream; - - simple = g_simple_async_result_new (G_OBJECT (helper->req), - helper->callback, helper->user_data, - conditional_get_ready_cb); - - if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) { - WebKitSoupCache *cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE); - - httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, msg); - if (httpstream) { - const gchar *content_type; - - g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref); - - soup_message_got_headers (helper->original); - - /* FIXME: Uncomment this when this becomes part of libsoup - * if (!soup_message_disables_feature(helper->original, SOUP_TYPE_CONTENT_SNIFFER)) { - * const gchar *content_type = soup_message_headers_get_content_type (msg->response_headers, NULL); - * soup_message_content_sniffed (helper->original, content_type, NULL); - * } - */ - content_type = soup_message_headers_get_content_type (msg->response_headers, NULL); - soup_message_content_sniffed (helper->original, content_type, NULL); - - g_simple_async_result_complete (simple); - - soup_message_finished (helper->original); - - g_object_unref (simple); - } else { - /* Ask again for the resource, somehow the cache cannot locate it */ - httpstream = webkit_soup_http_input_stream_new (session, helper->original); - webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT, - helper->cancellable, sent_async, simple); - } - } else { - /* It is in the cache but it was modified remotely */ - httpstream = webkit_soup_http_input_stream_new (session, helper->original); - webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT, - helper->cancellable, sent_async, simple); - } - - g_object_unref (helper->req); - g_object_unref (helper->original); - g_slice_free (ConditionalHelper, helper); -} - -typedef struct { - WebKitSoupRequestHTTP *http; - GAsyncReadyCallback callback; - gpointer user_data; - WebKitSoupHTTPInputStream *httpstream; -} SendAsyncHelper; - -static void webkit_soup_request_http_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - -static gboolean -send_async_cb (gpointer data) -{ - GSimpleAsyncResult *simple; - SendAsyncHelper *helper = (SendAsyncHelper *)data; - const gchar *content_type; - - simple = g_simple_async_result_new (G_OBJECT (helper->http), - helper->callback, helper->user_data, - webkit_soup_request_http_send_async); - g_simple_async_result_set_op_res_gpointer (simple, helper->httpstream, g_object_unref); - - /* Update message status */ - soup_message_set_status (helper->http->priv->msg, SOUP_STATUS_OK); - - /* Issue signals */ - soup_message_got_headers (helper->http->priv->msg); - - /* FIXME: Uncomment this when this becomes part of libsoup - * if (!soup_message_disables_feature(helper->http->priv->msg, SOUP_TYPE_CONTENT_SNIFFER)) { - * const gchar *content_type = soup_message_headers_get_content_type (helper->http->priv->msg->response_headers, NULL); - * soup_message_content_sniffed (helper->http->priv->msg, content_type, NULL); - * } - */ - content_type = soup_message_headers_get_content_type (helper->http->priv->msg->response_headers, NULL); - soup_message_content_sniffed (helper->http->priv->msg, content_type, NULL); - - g_simple_async_result_complete (simple); - - soup_message_finished (helper->http->priv->msg); - - g_object_unref (simple); - - g_object_unref (helper->http); - g_slice_free (SendAsyncHelper, helper); - - return FALSE; -} - -static void -webkit_soup_request_http_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request); - WebKitSoupHTTPInputStream *httpstream; - GSimpleAsyncResult *simple; - SoupSession *session; - WebKitSoupCache *cache; - - session = webkit_soup_request_get_session (request); - cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE); - - if (cache) { - WebKitSoupCacheResponse response; - - response = webkit_soup_cache_has_response (cache, http->priv->msg); - if (response == WEBKIT_SOUP_CACHE_RESPONSE_FRESH) { - WebKitSoupHTTPInputStream *httpstream; - - httpstream = (WebKitSoupHTTPInputStream *) - webkit_soup_cache_send_response (cache, SOUP_MESSAGE (http->priv->msg)); - - /* Cached resource file could have been deleted outside - */ - if (httpstream) { - /* Do return the stream asynchronously as in - * the other cases. It's not enough to use - * g_simple_async_result_complete_in_idle as - * the signals must be also emitted - * asynchronously - */ - SendAsyncHelper *helper = g_slice_new (SendAsyncHelper); - helper->http = g_object_ref (http); - helper->callback = callback; - helper->user_data = user_data; - helper->httpstream = httpstream; - g_timeout_add (0, send_async_cb, helper); - return; - } - } else if (response == WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) { - SoupMessage *conditional_msg; - ConditionalHelper *helper; - - conditional_msg = webkit_soup_cache_generate_conditional_request (cache, http->priv->msg); - - helper = g_slice_new0 (ConditionalHelper); - helper->req = g_object_ref (http); - helper->original = g_object_ref (http->priv->msg); - helper->cancellable = cancellable; - helper->callback = callback; - helper->user_data = user_data; - soup_session_queue_message (session, conditional_msg, - conditional_get_ready_cb, - helper); - return; - } - } - - simple = g_simple_async_result_new (G_OBJECT (http), - callback, user_data, - webkit_soup_request_http_send_async); - httpstream = webkit_soup_http_input_stream_new (webkit_soup_request_get_session (request), - http->priv->msg); - webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT, - cancellable, sent_async, simple); -} - -static GInputStream * -webkit_soup_request_http_send_finish (WebKitSoupRequest *request, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), webkit_soup_request_http_send_async) || g_simple_async_result_is_valid (result, G_OBJECT (request), conditional_get_ready_cb), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); -} - -static goffset -webkit_soup_request_http_get_content_length (WebKitSoupRequest *request) -{ - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request); - - return soup_message_headers_get_content_length (http->priv->msg->response_headers); -} - -static const char * -webkit_soup_request_http_get_content_type (WebKitSoupRequest *request) -{ - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request); - - return soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL); -} - -static void -webkit_soup_request_http_class_init (WebKitSoupRequestHTTPClass *request_http_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (request_http_class); - WebKitSoupRequestClass *request_class = - WEBKIT_SOUP_REQUEST_CLASS (request_http_class); - - g_type_class_add_private (request_http_class, sizeof (WebKitSoupRequestHTTPPrivate)); - - object_class->finalize = webkit_soup_request_http_finalize; - - request_class->check_uri = webkit_soup_request_http_check_uri; - request_class->send = webkit_soup_request_http_send; - request_class->send_async = webkit_soup_request_http_send_async; - request_class->send_finish = webkit_soup_request_http_send_finish; - request_class->get_content_length = webkit_soup_request_http_get_content_length; - request_class->get_content_type = webkit_soup_request_http_get_content_type; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-http.h b/Source/WebCore/platform/network/soup/cache/soup-request-http.h deleted file mode 100644 index a06a821..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-http.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_REQUEST_HTTP_H -#define WEBKIT_SOUP_REQUEST_HTTP_H 1 - -#include "soup-request.h" - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_REQUEST_HTTP (webkit_soup_request_http_get_type ()) -#define WEBKIT_SOUP_REQUEST_HTTP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTP)) -#define WEBKIT_SOUP_REQUEST_HTTP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPClass)) -#define WEBKIT_IS_SOUP_REQUEST_HTTP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_HTTP)) -#define WEBKIT_IS_SOUP_REQUEST_HTTP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_HTTP)) -#define WEBKIT_SOUP_REQUEST_HTTP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPClass)) - -typedef struct _WebKitSoupRequestHTTPPrivate WebKitSoupRequestHTTPPrivate; - -typedef struct { - WebKitSoupRequest parent; - - WebKitSoupRequestHTTPPrivate *priv; -} WebKitSoupRequestHTTP; - -typedef struct { - WebKitSoupRequestClass parent; -} WebKitSoupRequestHTTPClass; - -GType webkit_soup_request_http_get_type (void); - -SoupMessage *webkit_soup_request_http_get_message (WebKitSoupRequestHTTP *http); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_REQUEST_HTTP_H */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-request.c b/Source/WebCore/platform/network/soup/cache/soup-request.c deleted file mode 100644 index 46b9f5a..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request.c +++ /dev/null @@ -1,312 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-request.c: Protocol-independent streaming request interface - * - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010, Igalia S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <glib/gi18n.h> - -#include "soup-request.h" -#include "soup-requester.h" - -/** - * SECTION:soup-request - * @short_description: Protocol-independent streaming request interface - * - * FIXME - **/ - -/** - * WebKitSoupRequest: - * - * FIXME - * - * Since: 2.30 - **/ - -static void webkit_soup_request_initable_interface_init (GInitableIface *initable_interface); - -G_DEFINE_TYPE_WITH_CODE (WebKitSoupRequest, webkit_soup_request, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, - webkit_soup_request_initable_interface_init)) - -enum { - PROP_0, - PROP_URI, - PROP_SESSION -}; - -struct _WebKitSoupRequestPrivate { - SoupURI *uri; - SoupSession *session; -}; - -static void -webkit_soup_request_init (WebKitSoupRequest *request) -{ - request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestPrivate); -} - -static void -webkit_soup_request_finalize (GObject *object) -{ - WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object); - - if (request->priv->uri) - soup_uri_free (request->priv->uri); - if (request->priv->session) - g_object_unref (request->priv->session); - - G_OBJECT_CLASS (webkit_soup_request_parent_class)->finalize (object); -} - -static void -webkit_soup_request_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object); - - switch (prop_id) { - case PROP_URI: - if (request->priv->uri) - soup_uri_free (request->priv->uri); - request->priv->uri = g_value_dup_boxed (value); - break; - case PROP_SESSION: - if (request->priv->session) - g_object_unref (request->priv->session); - request->priv->session = g_value_dup_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -webkit_soup_request_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object); - - switch (prop_id) { - case PROP_URI: - g_value_set_boxed (value, request->priv->uri); - break; - case PROP_SESSION: - g_value_set_object (value, request->priv->session); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static gboolean -webkit_soup_request_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (initable); - gboolean ok; - - if (!request->priv->uri) { - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI, - _ ("No URI provided")); - return FALSE; - } - - ok = WEBKIT_SOUP_REQUEST_GET_CLASS (initable)-> - check_uri (request, request->priv->uri, error); - - if (!ok && error) { - char *uri_string = soup_uri_to_string (request->priv->uri, FALSE); - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI, - _ ("Invalid '%s' URI: %s"), - request->priv->uri->scheme, - uri_string); - g_free (uri_string); - } - - return ok; -} - -static gboolean -webkit_soup_request_default_check_uri (WebKitSoupRequest *request, - SoupURI *uri, - GError **error) -{ - return TRUE; -} - -/* Default implementation: assume the sync implementation doesn't block */ -static void -webkit_soup_request_default_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - - simple = g_simple_async_result_new (G_OBJECT (request), - callback, user_data, - webkit_soup_request_default_send_async); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); -} - -static GInputStream * -webkit_soup_request_default_send_finish (WebKitSoupRequest *request, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), webkit_soup_request_default_send_async), NULL); - - return webkit_soup_request_send (request, NULL, error); -} - -GInputStream * -webkit_soup_request_send (WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error) -{ - return WEBKIT_SOUP_REQUEST_GET_CLASS (request)-> - send (request, cancellable, error); -} - -void -webkit_soup_request_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - WEBKIT_SOUP_REQUEST_GET_CLASS (request)-> - send_async (request, cancellable, callback, user_data); -} - -GInputStream * -webkit_soup_request_send_finish (WebKitSoupRequest *request, - GAsyncResult *result, - GError **error) -{ - return WEBKIT_SOUP_REQUEST_GET_CLASS (request)-> - send_finish (request, result, error); -} - -static void -webkit_soup_request_class_init (WebKitSoupRequestClass *request_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (request_class); - - g_type_class_add_private (request_class, sizeof (WebKitSoupRequestPrivate)); - - request_class->check_uri = webkit_soup_request_default_check_uri; - request_class->send_async = webkit_soup_request_default_send_async; - request_class->send_finish = webkit_soup_request_default_send_finish; - - object_class->finalize = webkit_soup_request_finalize; - object_class->set_property = webkit_soup_request_set_property; - object_class->get_property = webkit_soup_request_get_property; - - g_object_class_install_property ( - object_class, PROP_URI, - g_param_spec_boxed (WEBKIT_SOUP_REQUEST_URI, - "URI", - "The request URI", - SOUP_TYPE_URI, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - g_object_class_install_property ( - object_class, PROP_SESSION, - g_param_spec_object (WEBKIT_SOUP_REQUEST_SESSION, - "Session", - "The request's session", - SOUP_TYPE_SESSION, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -} - -static void -webkit_soup_request_initable_interface_init (GInitableIface *initable_interface) -{ - initable_interface->init = webkit_soup_request_initable_init; -} - -SoupURI * -webkit_soup_request_get_uri (WebKitSoupRequest *request) -{ - return request->priv->uri; -} - -SoupSession * -webkit_soup_request_get_session (WebKitSoupRequest *request) -{ - return request->priv->session; -} - -goffset -webkit_soup_request_get_content_length (WebKitSoupRequest *request) -{ - return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->get_content_length (request); -} - -const char * -webkit_soup_request_get_content_type (WebKitSoupRequest *request) -{ - return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->get_content_type (request); -} - -#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10) -#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2])) - -/* Copy&pasted from libsoup's soup-uri.c after applying the patch in - * https://bugzilla.gnome.org/show_bug.cgi?id=630540. We need this - * instead of soup_uri_decode() as it incorrectly returns NULL for - * incorrectly encoded URLs. TODO: remove this when required libsoup - * version is bumped out to 2.32.1 - */ -gchar * -webkit_soup_request_uri_decoded_copy (const char *part, int length) -{ - unsigned char *s, *d; - char *decoded = g_strndup (part, length); - - s = d = (unsigned char *)decoded; - do { - if (*s == '%') { - if (!g_ascii_isxdigit (s[1]) || - !g_ascii_isxdigit (s[2])) { - *d++ = *s; - continue; - } - *d++ = HEXCHAR (s); - s += 2; - } else - *d++ = *s; - } while (*s++); - - return decoded; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-request.h b/Source/WebCore/platform/network/soup/cache/soup-request.h deleted file mode 100644 index 837d8f4..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request.h +++ /dev/null @@ -1,100 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_REQUEST_H -#define WEBKIT_SOUP_REQUEST_H 1 - -#include <libsoup/soup.h> -#include <gio/gio.h> - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_REQUEST (webkit_soup_request_get_type ()) -#define WEBKIT_SOUP_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequest)) -#define WEBKIT_SOUP_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestClass)) -#define WEBKIT_IS_SOUP_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUEST)) -#define WEBKIT_IS_SOUP_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST)) -#define WEBKIT_SOUP_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestClass)) - -typedef struct _WebKitSoupRequest WebKitSoupRequest; -typedef struct _WebKitSoupRequestPrivate WebKitSoupRequestPrivate; -typedef struct _WebKitSoupRequestClass WebKitSoupRequestClass; - -struct _WebKitSoupRequest { - GObject parent; - - WebKitSoupRequestPrivate *priv; -}; - -struct _WebKitSoupRequestClass { - GObjectClass parent; - - gboolean (*check_uri)(WebKitSoupRequest *req_base, - SoupURI *uri, - GError **error); - - GInputStream * (*send)(WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error); - void (*send_async)(WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - GInputStream * (*send_finish)(WebKitSoupRequest *request, - GAsyncResult *result, - GError **error); - - goffset (*get_content_length)(WebKitSoupRequest *request); - const char * (*get_content_type)(WebKitSoupRequest *request); -}; - -GType webkit_soup_request_get_type (void); - -#define WEBKIT_SOUP_REQUEST_URI "uri" -#define WEBKIT_SOUP_REQUEST_SESSION "session" - -GInputStream *webkit_soup_request_send (WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error); -void webkit_soup_request_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -GInputStream *webkit_soup_request_send_finish (WebKitSoupRequest *request, - GAsyncResult *result, - GError **error); - -SoupURI *webkit_soup_request_get_uri (WebKitSoupRequest *request); -SoupSession *webkit_soup_request_get_session (WebKitSoupRequest *request); - -goffset webkit_soup_request_get_content_length (WebKitSoupRequest *request); -const char *webkit_soup_request_get_content_type (WebKitSoupRequest *request); - -/* Used by WebKitSoupRequestFile and WebKitSoupRequestData. Ideally - * should be located in some util file but I'll place it here as it - * will be removed with libsoup 2.32.1 that will ship fixed versions - * of soup_uri_decode/normalize - */ -gchar *webkit_soup_request_uri_decoded_copy (const char *part, int length); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_REQUEST_H */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-requester.c b/Source/WebCore/platform/network/soup/cache/soup-requester.c deleted file mode 100644 index 4d8a860..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-requester.c +++ /dev/null @@ -1,188 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-requester.c: - * - * Copyright (C) 2010, Igalia S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "soup-requester.h" - -#include "soup-request-data.h" -#include "soup-request-file.h" -#include "soup-request-http.h" -#include <glib/gi18n.h> -#include <libsoup/soup.h> - -struct _WebKitSoupRequesterPrivate { - GHashTable *request_types; -}; - -#define WEBKIT_SOUP_REQUESTER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterPrivate)) - -G_DEFINE_TYPE (WebKitSoupRequester, webkit_soup_requester, G_TYPE_OBJECT) - -static void webkit_soup_requester_init (WebKitSoupRequester *requester) -{ - requester->priv = WEBKIT_SOUP_REQUESTER_GET_PRIVATE (requester); - - requester->priv->request_types = 0; -} - -static void finalize (GObject *object) -{ - WebKitSoupRequester *requester = WEBKIT_SOUP_REQUESTER (object); - - if (requester->priv->request_types) - g_hash_table_destroy (requester->priv->request_types); - - G_OBJECT_CLASS (webkit_soup_requester_parent_class)->finalize (object); -} - -static void webkit_soup_requester_class_init (WebKitSoupRequesterClass *requester_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (requester_class); - - g_type_class_add_private (requester_class, sizeof (WebKitSoupRequesterPrivate)); - - /* virtual method override */ - object_class->finalize = finalize; -} - -static void init_request_types (WebKitSoupRequesterPrivate *priv) -{ - if (priv->request_types) - return; - - priv->request_types = g_hash_table_new_full (soup_str_case_hash, - soup_str_case_equal, - g_free, 0); - g_hash_table_insert (priv->request_types, g_strdup ("file"), - GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_FILE)); - g_hash_table_insert (priv->request_types, g_strdup ("data"), - GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_DATA)); - g_hash_table_insert (priv->request_types, g_strdup ("http"), - GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_HTTP)); - g_hash_table_insert (priv->request_types, g_strdup ("https"), - GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_HTTP)); - g_hash_table_insert (priv->request_types, g_strdup ("ftp"), - GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_FILE)); -} - -WebKitSoupRequester *webkit_soup_requester_new (void) -{ - return (WebKitSoupRequester *)g_object_new (WEBKIT_TYPE_SOUP_REQUESTER, NULL); -} - -WebKitSoupRequest *webkit_soup_requester_request (WebKitSoupRequester *requester, const char *uriString, SoupSession *session, GError **error) -{ - SoupURI *uri = NULL; - WebKitSoupRequest *req; - - uri = soup_uri_new (uriString); - if (!uri) { - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI, - _ ("Could not parse URI '%s'"), uriString); - return 0; - } - - req = webkit_soup_requester_request_uri (requester, uri, session, error); - soup_uri_free (uri); - return req; -} - -WebKitSoupRequest *webkit_soup_requester_request_uri (WebKitSoupRequester *requester, SoupURI *uri, SoupSession *session, GError **error) -{ - GType requestType; - - g_return_val_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester), 0); - - init_request_types (requester->priv); - requestType = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (requester->priv->request_types, uri->scheme)); - if (!requestType) { - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME, - _ ("Unsupported URI scheme '%s'"), uri->scheme); - return 0; - } - - if (g_type_is_a (requestType, G_TYPE_INITABLE)) { - return (WebKitSoupRequest *)g_initable_new (requestType, 0, error, - "uri", uri, - "session", session, - NULL); - } else { - return (WebKitSoupRequest *)g_object_new (requestType, - "uri", uri, - "session", session, - NULL); - } -} - -/* RFC 2396, 3.1 */ -static gboolean -soup_scheme_is_valid (const char *scheme) -{ - if (scheme == NULL || - !g_ascii_isalpha (*scheme)) - return FALSE; - - scheme++; - while (*scheme) { - if (!g_ascii_isalpha (*scheme) && - !g_ascii_isdigit (*scheme) && - *scheme != '+' && - *scheme != '-' && - *scheme != '.') - return FALSE; - scheme++; - } - return TRUE; -} - -void -webkit_soup_requester_add_protocol (WebKitSoupRequester *requester, - const char *scheme, - GType request_type) -{ - g_return_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester)); - g_return_if_fail (soup_scheme_is_valid (scheme)); - - init_request_types (requester->priv); - g_hash_table_insert (requester->priv->request_types, g_strdup (scheme), - GSIZE_TO_POINTER (request_type)); -} - -void -webkit_soup_requester_remove_protocol (WebKitSoupRequester *requester, - const char *scheme) -{ - g_return_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester)); - g_return_if_fail (soup_scheme_is_valid (scheme)); - - init_request_types (requester->priv); - g_hash_table_remove (requester->priv->request_types, scheme); -} - -GQuark -webkit_soup_error_quark (void) -{ - static GQuark error; - if (!error) - error = g_quark_from_static_string ("webkit_soup_error_quark"); - return error; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-requester.h b/Source/WebCore/platform/network/soup/cache/soup-requester.h deleted file mode 100644 index 71ff103..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-requester.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2010 Igalia S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_REQUESTER_H -#define WEBKIT_SOUP_REQUESTER_H 1 - -#include "soup-request.h" -#include <libsoup/soup.h> - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_REQUESTER (webkit_soup_requester_get_type ()) -#define WEBKIT_SOUP_REQUESTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequester)) -#define WEBKIT_SOUP_REQUESTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterClass)) -#define WEBKIT_IS_SOUP_REQUESTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUESTER)) -#define WEBKIT_IS_SOUP_REQUESTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUESTER)) -#define WEBKIT_SOUP_REQUESTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterClass)) - -#define WEBKIT_SOUP_ERROR webkit_soup_error_quark () - -typedef enum { - WEBKIT_SOUP_ERROR_BAD_URI, - WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME -} WebKitSoupError; - -typedef struct _WebKitSoupRequester WebKitSoupRequester; -typedef struct _WebKitSoupRequesterPrivate WebKitSoupRequesterPrivate; - -struct _WebKitSoupRequester { - GObject parent; - - WebKitSoupRequesterPrivate *priv; -}; - -typedef struct { - GObjectClass parent_class; -} WebKitSoupRequesterClass; - -GType webkit_soup_requester_get_type (void); - -WebKitSoupRequester *webkit_soup_requester_new (void); - -GQuark webkit_soup_error_quark (void); - -WebKitSoupRequest *webkit_soup_requester_request (WebKitSoupRequester *requester, - const char *uriString, - SoupSession *session, - GError **error); - -WebKitSoupRequest *webkit_soup_requester_request_uri (WebKitSoupRequester *requester, - SoupURI *uri, - SoupSession *session, - GError **error); - -void webkit_soup_requester_add_protocol (WebKitSoupRequester *requester, - const char *scheme, - GType request_type); - -void webkit_soup_requester_remove_protocol (WebKitSoupRequester *requester, - const char *scheme); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_REQUESTER_H */ diff --git a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h b/Source/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h deleted file mode 100644 index 8af8de2..0000000 --- a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-cache-private.h: - * - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_CACHE_PRIVATE_H -#define WEBKIT_SOUP_CACHE_PRIVATE_H 1 - -#include "soup-cache.h" -#include <libsoup/soup-message.h> - -G_BEGIN_DECLS - -WebKitSoupCacheResponse webkit_soup_cache_has_response (WebKitSoupCache *cache, - SoupMessage *msg); -GInputStream *webkit_soup_cache_send_response (WebKitSoupCache *cache, - SoupMessage *msg); -WebKitSoupCacheability webkit_soup_cache_get_cacheability (WebKitSoupCache *cache, - SoupMessage *msg); -SoupMessage *webkit_soup_cache_generate_conditional_request (WebKitSoupCache *cache, - SoupMessage *original); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_CACHE_PRIVATE_H */ diff --git a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.c b/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.c deleted file mode 100644 index b96428d..0000000 --- a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.c +++ /dev/null @@ -1,1677 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-cache.c - * - * Copyright (C) 2009, 2010 Igalia S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/* TODO: - * - Need to hook the feature in the sync SoupSession. - * - Need more tests. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "soup-cache.h" -#include "soup-cache-private.h" -#include <libsoup/soup.h> -#include <gio/gio.h> -#include <stdlib.h> - -static SoupSessionFeatureInterface *webkit_soup_cache_default_feature_interface; -static void webkit_soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data); - -#define DEFAULT_MAX_SIZE 50 * 1024 * 1024 -#define MAX_ENTRY_DATA_PERCENTAGE 10 /* Percentage of the total size - of the cache that can be - filled by a single entry */ - -typedef struct _WebKitSoupCacheEntry { - char *key; - char *filename; - guint freshness_lifetime; - gboolean must_revalidate; - GString *data; - gsize pos; - gsize length; - time_t corrected_initial_age; - time_t response_time; - gboolean writing; - gboolean dirty; - gboolean got_body; - gboolean being_validated; - SoupMessageHeaders *headers; - GOutputStream *stream; - GError *error; - guint hits; - GCancellable *cancellable; -} WebKitSoupCacheEntry; - -struct _WebKitSoupCachePrivate { - char *cache_dir; - GHashTable *cache; - guint n_pending; - SoupSession *session; - WebKitSoupCacheType cache_type; - guint size; - guint max_size; - guint max_entry_data_size; /* Computed value. Here for performance reasons */ - GList *lru_start; -}; - -typedef struct { - WebKitSoupCache *cache; - WebKitSoupCacheEntry *entry; - SoupMessage *msg; - gulong got_chunk_handler; - gulong got_body_handler; - gulong restarted_handler; -} WebKitSoupCacheWritingFixture; - -enum { - PROP_0, - PROP_CACHE_DIR, - PROP_CACHE_TYPE -}; - -#define WEBKIT_SOUP_CACHE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCachePrivate)) - -G_DEFINE_TYPE_WITH_CODE (WebKitSoupCache, webkit_soup_cache, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, - webkit_soup_cache_session_feature_init)) - -static gboolean webkit_soup_cache_entry_remove (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry); -static void make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add); -static gboolean cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add); - -static WebKitSoupCacheability -get_cacheability (WebKitSoupCache *cache, SoupMessage *msg) -{ - WebKitSoupCacheability cacheability; - const char *cache_control; - - /* 1. The request method must be cacheable */ - if (msg->method == SOUP_METHOD_GET) - cacheability = WEBKIT_SOUP_CACHE_CACHEABLE; - else if (msg->method == SOUP_METHOD_HEAD || - msg->method == SOUP_METHOD_TRACE || - msg->method == SOUP_METHOD_CONNECT) - return WEBKIT_SOUP_CACHE_UNCACHEABLE; - else - return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES); - - cache_control = soup_message_headers_get (msg->response_headers, "Cache-Control"); - if (cache_control) { - GHashTable *hash; - WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache); - - hash = soup_header_parse_param_list (cache_control); - - /* Shared caches MUST NOT store private resources */ - if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) { - if (g_hash_table_lookup_extended (hash, "private", NULL, NULL)) { - soup_header_free_param_list (hash); - return WEBKIT_SOUP_CACHE_UNCACHEABLE; - } - } - - /* 2. The 'no-store' cache directive does not appear in the - * headers - */ - if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) { - soup_header_free_param_list (hash); - return WEBKIT_SOUP_CACHE_UNCACHEABLE; - } - - /* This does not appear in section 2.1, but I think it makes - * sense to check it too? - */ - if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) { - soup_header_free_param_list (hash); - return WEBKIT_SOUP_CACHE_UNCACHEABLE; - } - - soup_header_free_param_list (hash); - } - - switch (msg->status_code) { - case SOUP_STATUS_PARTIAL_CONTENT: - /* We don't cache partial responses, but they only - * invalidate cached full responses if the headers - * don't match. - */ - cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE; - break; - - case SOUP_STATUS_NOT_MODIFIED: - /* A 304 response validates an existing cache entry */ - cacheability = WEBKIT_SOUP_CACHE_VALIDATES; - break; - - case SOUP_STATUS_MULTIPLE_CHOICES: - case SOUP_STATUS_MOVED_PERMANENTLY: - case SOUP_STATUS_GONE: - /* FIXME: cacheable unless indicated otherwise */ - cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE; - break; - - case SOUP_STATUS_FOUND: - case SOUP_STATUS_TEMPORARY_REDIRECT: - /* FIXME: cacheable if explicitly indicated */ - cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE; - break; - - case SOUP_STATUS_SEE_OTHER: - case SOUP_STATUS_FORBIDDEN: - case SOUP_STATUS_NOT_FOUND: - case SOUP_STATUS_METHOD_NOT_ALLOWED: - return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES); - - default: - /* Any 5xx status or any 4xx status not handled above - * is uncacheable but doesn't break the cache. - */ - if ((msg->status_code >= SOUP_STATUS_BAD_REQUEST && - msg->status_code <= SOUP_STATUS_FAILED_DEPENDENCY) || - msg->status_code >= SOUP_STATUS_INTERNAL_SERVER_ERROR) - return WEBKIT_SOUP_CACHE_UNCACHEABLE; - - /* An unrecognized 2xx, 3xx, or 4xx response breaks - * the cache. - */ - if ((msg->status_code > SOUP_STATUS_PARTIAL_CONTENT && - msg->status_code < SOUP_STATUS_MULTIPLE_CHOICES) || - (msg->status_code > SOUP_STATUS_TEMPORARY_REDIRECT && - msg->status_code < SOUP_STATUS_INTERNAL_SERVER_ERROR)) - return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES); - break; - } - - return cacheability; -} - -static void -webkit_soup_cache_entry_free (WebKitSoupCacheEntry *entry, gboolean purge) -{ - if (purge) { - GFile *file = g_file_new_for_path (entry->filename); - g_file_delete (file, NULL, NULL); - g_object_unref (file); - } - - g_free (entry->filename); - entry->filename = NULL; - g_free (entry->key); - entry->key = NULL; - - if (entry->headers) { - soup_message_headers_free (entry->headers); - entry->headers = NULL; - } - - if (entry->data) { - g_string_free (entry->data, TRUE); - entry->data = NULL; - } - if (entry->error) { - g_error_free (entry->error); - entry->error = NULL; - } - if (entry->cancellable) { - g_object_unref (entry->cancellable); - entry->cancellable = NULL; - } - - g_slice_free (WebKitSoupCacheEntry, entry); -} - -static void -copy_headers (const char *name, const char *value, SoupMessageHeaders *headers) -{ - soup_message_headers_append (headers, name, value); -} - -static void -update_headers (const char *name, const char *value, SoupMessageHeaders *headers) -{ - if (soup_message_headers_get (headers, name)) - soup_message_headers_replace (headers, name, value); - else - soup_message_headers_append (headers, name, value); -} - -static guint -webkit_soup_cache_entry_get_current_age (WebKitSoupCacheEntry *entry) -{ - time_t now = time (NULL); - time_t resident_time; - - resident_time = now - entry->response_time; - return entry->corrected_initial_age + resident_time; -} - -static gboolean -webkit_soup_cache_entry_is_fresh_enough (WebKitSoupCacheEntry *entry, gint min_fresh) -{ - guint limit = (min_fresh == -1) ? webkit_soup_cache_entry_get_current_age (entry) : (guint) min_fresh; - return entry->freshness_lifetime > limit; -} - -static char * -soup_message_get_cache_key (SoupMessage *msg) -{ - SoupURI *uri = soup_message_get_uri (msg); - return soup_uri_to_string (uri, FALSE); -} - -static void -webkit_soup_cache_entry_set_freshness (WebKitSoupCacheEntry *entry, SoupMessage *msg, WebKitSoupCache *cache) -{ - const char *cache_control; - const char *expires, *date, *last_modified; - - cache_control = soup_message_headers_get (entry->headers, "Cache-Control"); - if (cache_control) { - const char *max_age, *s_maxage; - gint64 freshness_lifetime = 0; - GHashTable *hash; - WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache); - - hash = soup_header_parse_param_list (cache_control); - - /* Should we re-validate the entry when it goes stale */ - entry->must_revalidate = g_hash_table_lookup_extended (hash, "must-revalidate", NULL, NULL); - - /* Section 2.3.1 */ - if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) { - s_maxage = g_hash_table_lookup (hash, "s-maxage"); - if (s_maxage) { - freshness_lifetime = g_ascii_strtoll (s_maxage, NULL, 10); - if (freshness_lifetime) { - /* Implies proxy-revalidate. TODO: is it true? */ - entry->must_revalidate = TRUE; - soup_header_free_param_list (hash); - return; - } - } - } - - /* If 'max-age' cache directive is present, use that */ - max_age = g_hash_table_lookup (hash, "max-age"); - if (max_age) - freshness_lifetime = g_ascii_strtoll (max_age, NULL, 10); - - if (freshness_lifetime) { - entry->freshness_lifetime = (guint)MIN (freshness_lifetime, G_MAXUINT32); - soup_header_free_param_list (hash); - return; - } - - soup_header_free_param_list (hash); - } - - /* If the 'Expires' response header is present, use its value - * minus the value of the 'Date' response header - */ - expires = soup_message_headers_get (entry->headers, "Expires"); - date = soup_message_headers_get (entry->headers, "Date"); - if (expires && date) { - SoupDate *expires_d, *date_d; - time_t expires_t, date_t; - - expires_d = soup_date_new_from_string (expires); - if (expires_d) { - date_d = soup_date_new_from_string (date); - - expires_t = soup_date_to_time_t (expires_d); - date_t = soup_date_to_time_t (date_d); - - soup_date_free (expires_d); - soup_date_free (date_d); - - if (expires_t && date_t) { - entry->freshness_lifetime = (guint)MAX (expires_t - date_t, 0); - return; - } - } else { - /* If Expires is not a valid date we should - treat it as already expired, see section - 3.3 */ - entry->freshness_lifetime = 0; - return; - } - } - - /* Otherwise an heuristic may be used */ - - /* Heuristics MUST NOT be used with these status codes - (section 2.3.1.1) */ - if (msg->status_code != SOUP_STATUS_OK && - msg->status_code != SOUP_STATUS_NON_AUTHORITATIVE && - msg->status_code != SOUP_STATUS_PARTIAL_CONTENT && - msg->status_code != SOUP_STATUS_MULTIPLE_CHOICES && - msg->status_code != SOUP_STATUS_MOVED_PERMANENTLY && - msg->status_code != SOUP_STATUS_GONE) - goto expire; - - /* TODO: attach warning 113 if response's current_age is more - than 24h (section 2.3.1.1) when using heuristics */ - - /* Last-Modified based heuristic */ - last_modified = soup_message_headers_get (entry->headers, "Last-Modified"); - if (last_modified) { - SoupDate *soup_date; - time_t now, last_modified_t; - - soup_date = soup_date_new_from_string (last_modified); - last_modified_t = soup_date_to_time_t (soup_date); - now = time (NULL); - -#define HEURISTIC_FACTOR 0.1 /* From Section 2.3.1.1 */ - - entry->freshness_lifetime = MAX (0, (now - last_modified_t) * HEURISTIC_FACTOR); - soup_date_free (soup_date); - } - - return; - - expire: - /* If all else fails, make the entry expire immediately */ - entry->freshness_lifetime = 0; -} - -static WebKitSoupCacheEntry * -webkit_soup_cache_entry_new (WebKitSoupCache *cache, SoupMessage *msg, time_t request_time, time_t response_time) -{ - WebKitSoupCacheEntry *entry; - SoupMessageHeaders *headers; - const char *date; - char *md5; - - entry = g_slice_new0 (WebKitSoupCacheEntry); - entry->dirty = FALSE; - entry->writing = FALSE; - entry->got_body = FALSE; - entry->being_validated = FALSE; - entry->data = g_string_new (NULL); - entry->pos = 0; - entry->error = NULL; - - /* key & filename */ - entry->key = soup_message_get_cache_key (msg); - md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, entry->key, -1); - entry->filename = g_build_filename (cache->priv->cache_dir, md5, NULL); - g_free (md5); - - /* Headers */ - headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); - soup_message_headers_foreach (msg->response_headers, - (SoupMessageHeadersForeachFunc)copy_headers, - headers); - entry->headers = headers; - - /* LRU list */ - entry->hits = 0; - - /* Section 2.3.1, Freshness Lifetime */ - webkit_soup_cache_entry_set_freshness (entry, msg, cache); - - /* Section 2.3.2, Calculating Age */ - date = soup_message_headers_get (entry->headers, "Date"); - - if (date) { - SoupDate *soup_date; - const char *age; - time_t date_value, apparent_age, corrected_received_age, response_delay, age_value = 0; - - soup_date = soup_date_new_from_string (date); - date_value = soup_date_to_time_t (soup_date); - soup_date_free (soup_date); - - age = soup_message_headers_get (entry->headers, "Age"); - if (age) - age_value = g_ascii_strtoll (age, NULL, 10); - - entry->response_time = response_time; - apparent_age = MAX (0, entry->response_time - date_value); - corrected_received_age = MAX (apparent_age, age_value); - response_delay = entry->response_time - request_time; - entry->corrected_initial_age = corrected_received_age + response_delay; - } else { - /* Is this correct ? */ - entry->corrected_initial_age = time (NULL); - } - - return entry; -} - -static void -webkit_soup_cache_writing_fixture_free (WebKitSoupCacheWritingFixture *fixture) -{ - /* Free fixture. And disconnect signals, we don't want to - listen to more SoupMessage events as we're finished with - this resource */ - if (g_signal_handler_is_connected (fixture->msg, fixture->got_chunk_handler)) - g_signal_handler_disconnect (fixture->msg, fixture->got_chunk_handler); - if (g_signal_handler_is_connected (fixture->msg, fixture->got_body_handler)) - g_signal_handler_disconnect (fixture->msg, fixture->got_body_handler); - if (g_signal_handler_is_connected (fixture->msg, fixture->restarted_handler)) - g_signal_handler_disconnect (fixture->msg, fixture->restarted_handler); - g_object_unref (fixture->msg); - g_object_unref (fixture->cache); - g_slice_free (WebKitSoupCacheWritingFixture, fixture); -} - -static void -close_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture) -{ - WebKitSoupCacheEntry *entry = fixture->entry; - WebKitSoupCache *cache = fixture->cache; - GOutputStream *stream = G_OUTPUT_STREAM (source); - goffset content_length; - - g_warn_if_fail (entry->error == NULL); - - /* FIXME: what do we do on error ? */ - - if (stream) { - g_output_stream_close_finish (stream, result, NULL); - g_object_unref (stream); - } - entry->stream = NULL; - - content_length = soup_message_headers_get_content_length (entry->headers); - - /* If the process was cancelled, then delete the entry from - the cache. Do it also if the size of a chunked resource is - too much for the cache */ - if (g_cancellable_is_cancelled (entry->cancellable)) { - entry->dirty = FALSE; - webkit_soup_cache_entry_remove (cache, entry); - webkit_soup_cache_entry_free (entry, TRUE); - entry = NULL; - } else if ((soup_message_headers_get_encoding (entry->headers) == SOUP_ENCODING_CHUNKED) || - entry->length != (gsize) content_length) { - /** Two options here: - * - * 1. "chunked" data, entry was temporarily added to - * cache (as content-length is 0) and now that we have - * the actual size we have to evaluate if we want it - * in the cache or not - * - * 2. Content-Length has a different value than actual - * length, means that the content was encoded for - * transmission (typically compressed) and thus we - * have to substract the content-length value that was - * added to the cache and add the unencoded length - **/ - gint length_to_add = entry->length - content_length; - - /* Make room in cache if needed */ - if (cache_accepts_entries_of_size (cache, length_to_add)) { - make_room_for_new_entry (cache, length_to_add); - - cache->priv->size += length_to_add; - } else { - entry->dirty = FALSE; - webkit_soup_cache_entry_remove (cache, entry); - webkit_soup_cache_entry_free (entry, TRUE); - entry = NULL; - } - } - - if (entry) { - /* Get rid of the GString in memory for the resource now */ - if (entry->data) { - g_string_free (entry->data, TRUE); - entry->data = NULL; - } - - entry->dirty = FALSE; - entry->writing = FALSE; - entry->got_body = FALSE; - entry->pos = 0; - - g_object_unref (entry->cancellable); - entry->cancellable = NULL; - } - - cache->priv->n_pending--; - - /* Frees */ - webkit_soup_cache_writing_fixture_free (fixture); -} - -static void -write_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture) -{ - GOutputStream *stream = G_OUTPUT_STREAM (source); - GError *error = NULL; - gssize write_size; - WebKitSoupCacheEntry *entry = fixture->entry; - - if (g_cancellable_is_cancelled (entry->cancellable)) { - g_output_stream_close_async (stream, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)close_ready_cb, - fixture); - return; - } - - write_size = g_output_stream_write_finish (stream, result, &error); - if (write_size <= 0 || error) { - if (error) - entry->error = error; - g_output_stream_close_async (stream, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)close_ready_cb, - fixture); - /* FIXME: We should completely stop caching the - resource at this point */ - } else { - entry->pos += write_size; - - /* Are we still writing and is there new data to write - already ? */ - if (entry->data && entry->pos < entry->data->len) { - g_output_stream_write_async (entry->stream, - entry->data->str + entry->pos, - entry->data->len - entry->pos, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)write_ready_cb, - fixture); - } else { - entry->writing = FALSE; - - if (entry->got_body) { - /* If we already received 'got-body' - and we have written all the data, - we can close the stream */ - g_output_stream_close_async (entry->stream, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)close_ready_cb, - fixture); - } - } - } -} - -static void -msg_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, WebKitSoupCacheWritingFixture *fixture) -{ - WebKitSoupCacheEntry *entry = fixture->entry; - - g_return_if_fail (chunk->data && chunk->length); - g_return_if_fail (entry); - - /* Ignore this if the writing or appending was cancelled */ - if (!g_cancellable_is_cancelled (entry->cancellable)) { - g_string_append_len (entry->data, chunk->data, chunk->length); - entry->length = entry->data->len; - - if (!cache_accepts_entries_of_size (fixture->cache, entry->length)) { - /* Quickly cancel the caching of the resource */ - g_cancellable_cancel (entry->cancellable); - } - } - - /* FIXME: remove the error check when we cancel the caching at - the first write error */ - /* Only write if the entry stream is ready */ - if (entry->writing == FALSE && entry->error == NULL && entry->stream) { - GString *data = entry->data; - entry->writing = TRUE; - g_output_stream_write_async (entry->stream, - data->str + entry->pos, - data->len - entry->pos, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)write_ready_cb, - fixture); - } -} - -static void -msg_got_body_cb (SoupMessage *msg, WebKitSoupCacheWritingFixture *fixture) -{ - WebKitSoupCacheEntry *entry = fixture->entry; - g_return_if_fail (entry); - - entry->got_body = TRUE; - - if (!entry->stream && entry->pos != entry->length) - /* The stream is not ready to be written but we still - have data to write, we'll write it when the stream - is opened for writing */ - return; - - - if (entry->pos != entry->length) { - /* If we still have data to write, write it, - write_ready_cb will close the stream */ - if (entry->writing == FALSE && entry->error == NULL && entry->stream) { - g_output_stream_write_async (entry->stream, - entry->data->str + entry->pos, - entry->data->len - entry->pos, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)write_ready_cb, - fixture); - } - return; - } - - if (entry->stream && !entry->writing) - g_output_stream_close_async (entry->stream, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)close_ready_cb, - fixture); -} - -static gboolean -webkit_soup_cache_entry_remove (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry) -{ - GList *lru_item; - - /* if (entry->dirty && !g_cancellable_is_cancelled (entry->cancellable)) { */ - if (entry->dirty) { - g_cancellable_cancel (entry->cancellable); - return FALSE; - } - - g_assert (!entry->dirty); - g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache)); - - /* Remove from cache */ - if (!g_hash_table_remove (cache->priv->cache, entry->key)) - return FALSE; - - /* Remove from LRU */ - lru_item = g_list_find (cache->priv->lru_start, entry); - cache->priv->lru_start = g_list_delete_link (cache->priv->lru_start, lru_item); - - /* Adjust cache size */ - cache->priv->size -= entry->length; - - g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache)); - - return TRUE; -} - -static gint -lru_compare_func (gconstpointer a, gconstpointer b) -{ - WebKitSoupCacheEntry *entry_a = (WebKitSoupCacheEntry *)a; - WebKitSoupCacheEntry *entry_b = (WebKitSoupCacheEntry *)b; - - /** The rationale of this sorting func is - * - * 1. sort by hits -> LRU algorithm, then - * - * 2. sort by freshness lifetime, we better discard first - * entries that are close to expire - * - * 3. sort by size, replace first small size resources as they - * are cheaper to download - **/ - - /* Sort by hits */ - if (entry_a->hits != entry_b->hits) - return entry_a->hits - entry_b->hits; - - /* Sort by freshness_lifetime */ - if (entry_a->freshness_lifetime != entry_b->freshness_lifetime) - return entry_a->freshness_lifetime - entry_b->freshness_lifetime; - - /* Sort by size */ - return entry_a->length - entry_b->length; -} - -static gboolean -cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add) -{ - /* We could add here some more heuristics. TODO: review how - this is done by other HTTP caches */ - - return length_to_add <= cache->priv->max_entry_data_size; -} - -static void -make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add) -{ - GList *lru_entry = cache->priv->lru_start; - - /* Check that there is enough room for the new entry. This is - an approximation as we're not working out the size of the - cache file or the size of the headers for performance - reasons. TODO: check if that would be really that expensive */ - - while (lru_entry && - (length_to_add + cache->priv->size > cache->priv->max_size)) { - WebKitSoupCacheEntry *old_entry = (WebKitSoupCacheEntry *)lru_entry->data; - - /* Discard entries. Once cancelled resources will be - * freed in close_ready_cb - */ - if (webkit_soup_cache_entry_remove (cache, old_entry)) { - webkit_soup_cache_entry_free (old_entry, TRUE); - lru_entry = cache->priv->lru_start; - } else - lru_entry = g_list_next (lru_entry); - } -} - -static gboolean -webkit_soup_cache_entry_insert_by_key (WebKitSoupCache *cache, - const char *key, - WebKitSoupCacheEntry *entry, - gboolean sort) -{ - guint length_to_add = 0; - - if (soup_message_headers_get_encoding (entry->headers) != SOUP_ENCODING_CHUNKED) - length_to_add = soup_message_headers_get_content_length (entry->headers); - - /* Check if we are going to store the resource depending on its size */ - if (length_to_add) { - if (!cache_accepts_entries_of_size (cache, length_to_add)) - return FALSE; - - /* Make room for new entry if needed */ - make_room_for_new_entry (cache, length_to_add); - } - - g_hash_table_insert (cache->priv->cache, g_strdup (key), entry); - - /* Compute new cache size */ - cache->priv->size += length_to_add; - - /* Update LRU */ - if (sort) - cache->priv->lru_start = g_list_insert_sorted (cache->priv->lru_start, entry, lru_compare_func); - else - cache->priv->lru_start = g_list_prepend (cache->priv->lru_start, entry); - - g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache)); - - return TRUE; -} - -static void -msg_restarted_cb (SoupMessage *msg, WebKitSoupCacheEntry *entry) -{ - /* FIXME: What should we do here exactly? */ -} - -static void -append_to_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture) -{ - GFile *file = (GFile *)source; - GOutputStream *stream; - WebKitSoupCacheEntry *entry = fixture->entry; - - stream = (GOutputStream *)g_file_append_to_finish (file, result, &entry->error); - - if (g_cancellable_is_cancelled (entry->cancellable) || entry->error) { - fixture->cache->priv->n_pending--; - entry->dirty = FALSE; - webkit_soup_cache_entry_remove (fixture->cache, entry); - webkit_soup_cache_entry_free (entry, TRUE); - webkit_soup_cache_writing_fixture_free (fixture); - return; - } - - entry->stream = g_object_ref (stream); - g_object_unref (file); - - /* If we already got all the data we have to initiate the - writing here, since we won't get more 'got-chunk' - signals */ - if (entry->got_body) { - GString *data = entry->data; - - /* It could happen that reading the data from server - was completed before this happens. In that case - there is no data */ - if (data) { - entry->writing = TRUE; - g_output_stream_write_async (entry->stream, - data->str + entry->pos, - data->len - entry->pos, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)write_ready_cb, - fixture); - } - } -} - -typedef struct { - time_t request_time; - SoupSessionFeature *feature; - gulong got_headers_handler; -} RequestHelper; - -static void -msg_got_headers_cb (SoupMessage *msg, gpointer user_data) -{ - WebKitSoupCache *cache; - WebKitSoupCacheability cacheable; - RequestHelper *helper; - time_t request_time, response_time; - - response_time = time (NULL); - - helper = (RequestHelper *)user_data; - cache = WEBKIT_SOUP_CACHE (helper->feature); - request_time = helper->request_time; - g_signal_handlers_disconnect_by_func (msg, msg_got_headers_cb, user_data); - g_slice_free (RequestHelper, helper); - - cacheable = webkit_soup_cache_get_cacheability (cache, msg); - - if (cacheable & WEBKIT_SOUP_CACHE_CACHEABLE) { - WebKitSoupCacheEntry *entry; - char *key; - GFile *file; - WebKitSoupCacheWritingFixture *fixture; - - /* Check if we are already caching this resource */ - key = soup_message_get_cache_key (msg); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - - if (entry && entry->dirty) - return; - - /* Create a new entry, deleting any old one if present */ - if (entry) { - webkit_soup_cache_entry_remove (cache, entry); - webkit_soup_cache_entry_free (entry, TRUE); - } - - entry = webkit_soup_cache_entry_new (cache, msg, request_time, response_time); - entry->hits = 1; - - /* Do not continue if it can not be stored */ - if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry, TRUE)) { - webkit_soup_cache_entry_free (entry, TRUE); - return; - } - - fixture = g_slice_new0 (WebKitSoupCacheWritingFixture); - fixture->cache = g_object_ref (cache); - fixture->entry = entry; - fixture->msg = g_object_ref (msg); - - /* We connect now to these signals and buffer the data - if it comes before the file is ready for writing */ - fixture->got_chunk_handler = - g_signal_connect (msg, "got-chunk", G_CALLBACK (msg_got_chunk_cb), fixture); - fixture->got_body_handler = - g_signal_connect (msg, "got-body", G_CALLBACK (msg_got_body_cb), fixture); - fixture->restarted_handler = - g_signal_connect (msg, "restarted", G_CALLBACK (msg_restarted_cb), entry); - - /* Prepare entry */ - file = g_file_new_for_path (entry->filename); - cache->priv->n_pending++; - - entry->dirty = TRUE; - entry->cancellable = g_cancellable_new (); - g_file_append_to_async (file, 0, - G_PRIORITY_LOW, entry->cancellable, - (GAsyncReadyCallback)append_to_ready_cb, - fixture); - } else if (cacheable & WEBKIT_SOUP_CACHE_INVALIDATES) { - char *key; - WebKitSoupCacheEntry *entry; - - key = soup_message_get_cache_key (msg); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - - if (entry) { - if (webkit_soup_cache_entry_remove (cache, entry)) - webkit_soup_cache_entry_free (entry, TRUE); - } - } else if (cacheable & WEBKIT_SOUP_CACHE_VALIDATES) { - char *key; - WebKitSoupCacheEntry *entry; - - key = soup_message_get_cache_key (msg); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - - /* It's possible to get a CACHE_VALIDATES with no - * entry in the hash table. This could happen if for - * example the soup client is the one creating the - * conditional request. - */ - if (entry) { - entry->being_validated = FALSE; - - /* We update the headers of the existing cache item, - plus its age */ - soup_message_headers_foreach (msg->response_headers, - (SoupMessageHeadersForeachFunc)update_headers, - entry->headers); - webkit_soup_cache_entry_set_freshness (entry, msg, cache); - } - } -} - -GInputStream * -webkit_soup_cache_send_response (WebKitSoupCache *cache, SoupMessage *msg) -{ - char *key; - WebKitSoupCacheEntry *entry; - char *current_age; - GInputStream *stream = NULL; - GFile *file; - - g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL); - g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL); - - key = soup_message_get_cache_key (msg); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - g_return_val_if_fail (entry, NULL); - - /* If we are told to send a response from cache any validation - in course is over by now */ - entry->being_validated = FALSE; - - /* Headers */ - soup_message_headers_foreach (entry->headers, - (SoupMessageHeadersForeachFunc)update_headers, - msg->response_headers); - - /* Add 'Age' header with the current age */ - current_age = g_strdup_printf ("%d", webkit_soup_cache_entry_get_current_age (entry)); - soup_message_headers_replace (msg->response_headers, - "Age", - current_age); - g_free (current_age); - - /* TODO: the original idea was to save reads, but current code - assumes that a stream is always returned. Need to reach - some agreement here. Also we have to handle the situation - were the file was no longer there (for example files - removed without notifying the cache */ - file = g_file_new_for_path (entry->filename); - stream = (GInputStream *)g_file_read (file, NULL, NULL); - - return stream; -} - -static void -request_started (SoupSessionFeature *feature, SoupSession *session, - SoupMessage *msg, SoupSocket *socket) -{ - RequestHelper *helper = g_slice_new0 (RequestHelper); - helper->request_time = time (NULL); - helper->feature = feature; - helper->got_headers_handler = g_signal_connect (msg, "got-headers", - G_CALLBACK (msg_got_headers_cb), - helper); -} - -static void -attach (SoupSessionFeature *feature, SoupSession *session) -{ - WebKitSoupCache *cache = WEBKIT_SOUP_CACHE (feature); - cache->priv->session = session; - - webkit_soup_cache_default_feature_interface->attach (feature, session); -} - -static void -webkit_soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface, - gpointer interface_data) -{ - webkit_soup_cache_default_feature_interface = - g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE); - - feature_interface->attach = attach; - feature_interface->request_started = request_started; -} - -static void -webkit_soup_cache_init (WebKitSoupCache *cache) -{ - WebKitSoupCachePrivate *priv; - - priv = cache->priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache); - - priv->cache = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify)g_free, - NULL); - - /* LRU */ - priv->lru_start = NULL; - - /* */ - priv->n_pending = 0; - - /* Cache size */ - priv->max_size = DEFAULT_MAX_SIZE; - priv->max_entry_data_size = priv->max_size / MAX_ENTRY_DATA_PERCENTAGE; - priv->size = 0; -} - -static void -remove_cache_item (gpointer data, - gpointer user_data) -{ - WebKitSoupCache *cache = (WebKitSoupCache *) user_data; - WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data; - - if (webkit_soup_cache_entry_remove (cache, entry)) - webkit_soup_cache_entry_free (entry, FALSE); -} - -static void -webkit_soup_cache_finalize (GObject *object) -{ - WebKitSoupCachePrivate *priv; - GList *entries; - - priv = WEBKIT_SOUP_CACHE (object)->priv; - - // Cannot use g_hash_table_foreach as callbacks must not modify the hash table - entries = g_hash_table_get_values (priv->cache); - g_list_foreach (entries, remove_cache_item, object); - g_list_free (entries); - - g_hash_table_destroy (priv->cache); - g_free (priv->cache_dir); - - g_list_free (priv->lru_start); - priv->lru_start = NULL; - - G_OBJECT_CLASS (webkit_soup_cache_parent_class)->finalize (object); -} - -static void -webkit_soup_cache_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv; - - switch (prop_id) { - case PROP_CACHE_DIR: - priv->cache_dir = g_value_dup_string (value); - /* Create directory if it does not exist (FIXME: should we?) */ - if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) - g_mkdir_with_parents (priv->cache_dir, 0700); - break; - case PROP_CACHE_TYPE: - priv->cache_type = g_value_get_enum (value); - /* TODO: clear private entries and issue a warning if moving to shared? */ - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -webkit_soup_cache_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv; - - switch (prop_id) { - case PROP_CACHE_DIR: - g_value_set_string (value, priv->cache_dir); - break; - case PROP_CACHE_TYPE: - g_value_set_enum (value, priv->cache_type); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -webkit_soup_cache_constructed (GObject *object) -{ - WebKitSoupCachePrivate *priv; - - priv = WEBKIT_SOUP_CACHE (object)->priv; - - if (!priv->cache_dir) { - /* Set a default cache dir, different for each user */ - priv->cache_dir = g_build_filename (g_get_user_cache_dir (), - "httpcache", - NULL); - if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) - g_mkdir_with_parents (priv->cache_dir, 0700); - } - - if (G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed) - G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed (object); -} - -#define WEBKIT_SOUP_CACHE_TYPE_TYPE (webkit_soup_cache_type_get_type ()) -static GType -webkit_soup_cache_type_get_type (void) -{ - static GType cache_type = 0; - - static const GEnumValue cache_types[] = { - { WEBKIT_SOUP_CACHE_SINGLE_USER, "Single user cache", "user" }, - { WEBKIT_SOUP_CACHE_SHARED, "Shared cache", "shared" }, - { 0, NULL, NULL } - }; - - if (!cache_type) { - cache_type = g_enum_register_static ("WebKitSoupCacheTypeType", cache_types); - } - return cache_type; -} - -static void -webkit_soup_cache_class_init (WebKitSoupCacheClass *cache_class) -{ - GObjectClass *gobject_class = (GObjectClass *)cache_class; - - gobject_class->finalize = webkit_soup_cache_finalize; - gobject_class->constructed = webkit_soup_cache_constructed; - gobject_class->set_property = webkit_soup_cache_set_property; - gobject_class->get_property = webkit_soup_cache_get_property; - - cache_class->get_cacheability = get_cacheability; - - g_object_class_install_property (gobject_class, PROP_CACHE_DIR, - g_param_spec_string ("cache-dir", - "Cache directory", - "The directory to store the cache files", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (gobject_class, PROP_CACHE_TYPE, - g_param_spec_enum ("cache-type", - "Cache type", - "Whether the cache is private or shared", - WEBKIT_SOUP_CACHE_TYPE_TYPE, - WEBKIT_SOUP_CACHE_SINGLE_USER, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_type_class_add_private (cache_class, sizeof (WebKitSoupCachePrivate)); -} - -/** - * webkit_soup_cache_new: - * @cache_dir: the directory to store the cached data, or %NULL to use the default one - * @cache_type: the #WebKitSoupCacheType of the cache - * - * Creates a new #WebKitSoupCache. - * - * Returns: a new #WebKitSoupCache - * - * Since: 2.28 - **/ -WebKitSoupCache * -webkit_soup_cache_new (const char *cache_dir, WebKitSoupCacheType cache_type) -{ - return g_object_new (WEBKIT_TYPE_SOUP_CACHE, - "cache-dir", cache_dir, - "cache-type", cache_type, - NULL); -} - -/** - * webkit_soup_cache_has_response: - * @cache: a #WebKitSoupCache - * @msg: a #SoupMessage - * - * This function calculates whether the @cache object has a proper - * response for the request @msg given the flags both in the request - * and the cached reply and the time ellapsed since it was cached. - * - * Returns: whether or not the @cache has a valid response for @msg - **/ -WebKitSoupCacheResponse -webkit_soup_cache_has_response (WebKitSoupCache *cache, SoupMessage *msg) -{ - char *key; - WebKitSoupCacheEntry *entry; - const char *cache_control; - gpointer value; - gboolean must_revalidate; - int max_age, max_stale, min_fresh; - GList *lru_item, *item; - - key = soup_message_get_cache_key (msg); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - - /* 1. The presented Request-URI and that of stored response - * match - */ - if (!entry) - return WEBKIT_SOUP_CACHE_RESPONSE_STALE; - - /* Increase hit count. Take sorting into account */ - entry->hits++; - lru_item = g_list_find (cache->priv->lru_start, entry); - item = lru_item; - while (item->next && lru_compare_func (item->data, item->next->data) > 0) - item = g_list_next (item); - - if (item != lru_item) { - cache->priv->lru_start = g_list_remove_link (cache->priv->lru_start, lru_item); - item = g_list_insert_sorted (item, lru_item->data, lru_compare_func); - g_list_free (lru_item); - } - - if (entry->dirty || entry->being_validated) - return WEBKIT_SOUP_CACHE_RESPONSE_STALE; - - /* 2. The request method associated with the stored response - * allows it to be used for the presented request - */ - - /* In practice this means we only return our resource for GET, - * cacheability for other methods is a TODO in the RFC - * (TODO: although we could return the headers for HEAD - * probably). - */ - if (msg->method != SOUP_METHOD_GET) - return WEBKIT_SOUP_CACHE_RESPONSE_STALE; - - /* 3. Selecting request-headers nominated by the stored - * response (if any) match those presented. - */ - - /* TODO */ - - /* 4. The request is a conditional request issued by the client. - */ - if (soup_message_headers_get (msg->request_headers, "If-Modified-Since") || - soup_message_headers_get (msg->request_headers, "If-None-Match")) - return WEBKIT_SOUP_CACHE_RESPONSE_STALE; - - /* 5. The presented request and stored response are free from - * directives that would prevent its use. - */ - - must_revalidate = FALSE; - max_age = max_stale = min_fresh = -1; - - cache_control = soup_message_headers_get (msg->request_headers, "Cache-Control"); - if (cache_control) { - GHashTable *hash = soup_header_parse_param_list (cache_control); - - if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) { - soup_header_free_param_list (hash); - return WEBKIT_SOUP_CACHE_RESPONSE_STALE; - } - - if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) { - entry->must_revalidate = TRUE; - } - - if (g_hash_table_lookup_extended (hash, "max-age", NULL, &value)) { - max_age = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32); - } - - /* max-stale can have no value set, we need to use _extended */ - if (g_hash_table_lookup_extended (hash, "max-stale", NULL, &value)) { - if (value) - max_stale = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32); - else - max_stale = G_MAXINT32; - } - - value = g_hash_table_lookup (hash, "min-fresh"); - if (value) - min_fresh = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32); - - soup_header_free_param_list (hash); - - if (max_age != -1) { - guint current_age = webkit_soup_cache_entry_get_current_age (entry); - - /* If we are over max-age and max-stale is not - set, do not use the value from the cache - without validation */ - if ((guint) max_age <= current_age && max_stale == -1) - return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION; - } - } - - /* 6. The stored response is either: fresh, allowed to be - * served stale or succesfully validated - */ - /* TODO consider also proxy-revalidate & s-maxage */ - if (entry->must_revalidate) - return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION; - - if (!webkit_soup_cache_entry_is_fresh_enough (entry, min_fresh)) { - /* Not fresh, can it be served stale? */ - if (max_stale != -1) { - /* G_MAXINT32 means we accept any staleness */ - if (max_stale == G_MAXINT32) - return WEBKIT_SOUP_CACHE_RESPONSE_FRESH; - - if ((webkit_soup_cache_entry_get_current_age (entry) - entry->freshness_lifetime) <= (guint) max_stale) - return WEBKIT_SOUP_CACHE_RESPONSE_FRESH; - } - - return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION; - } - - return WEBKIT_SOUP_CACHE_RESPONSE_FRESH; -} - -/** - * webkit_soup_cache_get_cacheability: - * @cache: a #WebKitSoupCache - * @msg: a #SoupMessage - * - * Calculates whether the @msg can be cached or not. - * - * Returns: a #WebKitSoupCacheability value indicating whether the @msg can be cached or not. - **/ -WebKitSoupCacheability -webkit_soup_cache_get_cacheability (WebKitSoupCache *cache, SoupMessage *msg) -{ - g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), WEBKIT_SOUP_CACHE_UNCACHEABLE); - g_return_val_if_fail (SOUP_IS_MESSAGE (msg), WEBKIT_SOUP_CACHE_UNCACHEABLE); - - return WEBKIT_SOUP_CACHE_GET_CLASS (cache)->get_cacheability (cache, msg); -} - -static gboolean -force_flush_timeout (gpointer data) -{ - gboolean *forced = (gboolean *)data; - *forced = TRUE; - - return FALSE; -} - -/** - * webkit_soup_cache_flush: - * @cache: a #WebKitSoupCache - * @session: the #SoupSession associated with the @cache - * - * This function will force all pending writes in the @cache to be - * committed to disk. For doing so it will iterate the #GMainContext - * associated with the @session (which can be the default one) as long - * as needed. - **/ -void -webkit_soup_cache_flush (WebKitSoupCache *cache) -{ - GMainContext *async_context; - SoupSession *session; - guint timeout_id; - gboolean forced = FALSE; - - g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache)); - - session = cache->priv->session; - g_return_if_fail (SOUP_IS_SESSION (session)); - async_context = soup_session_get_async_context (session); - - /* We give cache 10 secs to finish */ - timeout_id = g_timeout_add (10000, force_flush_timeout, &forced); - - while (!forced && cache->priv->n_pending > 0) - g_main_context_iteration (async_context, FALSE); - - if (!forced) - g_source_remove (timeout_id); - else - g_warning ("Cache flush finished despite %d pending requests", cache->priv->n_pending); -} - -static void -clear_cache_item (gpointer data, - gpointer user_data) -{ - WebKitSoupCache *cache = (WebKitSoupCache *) user_data; - WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data; - - if (webkit_soup_cache_entry_remove (cache, entry)) - webkit_soup_cache_entry_free (entry, TRUE); -} - -/** - * webkit_soup_cache_clear: - * @cache: a #WebKitSoupCache - * - * Will remove all entries in the @cache plus all the cache files - * associated with them. - **/ -void -webkit_soup_cache_clear (WebKitSoupCache *cache) -{ - GHashTable *hash; - GList *entries; - - g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache)); - - hash = cache->priv->cache; - g_return_if_fail (hash); - - // Cannot use g_hash_table_foreach as callbacks must not modify the hash table - entries = g_hash_table_get_values (hash); - g_list_foreach (entries, clear_cache_item, cache); - g_list_free (entries); -} - -SoupMessage * -webkit_soup_cache_generate_conditional_request (WebKitSoupCache *cache, SoupMessage *original) -{ - SoupMessage *msg; - SoupURI *uri; - WebKitSoupCacheEntry *entry; - char *key; - const char *value; - - g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL); - g_return_val_if_fail (SOUP_IS_MESSAGE (original), NULL); - - /* First copy the data we need from the original message */ - uri = soup_message_get_uri (original); - msg = soup_message_new_from_uri (original->method, uri); - - soup_message_headers_foreach (original->request_headers, - (SoupMessageHeadersForeachFunc)copy_headers, - msg->request_headers); - - /* Now add the validator entries in the header from the cached - data */ - key = soup_message_get_cache_key (original); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - - g_return_val_if_fail (entry, NULL); - - entry->being_validated = TRUE; - - value = soup_message_headers_get (entry->headers, "Last-Modified"); - if (value) - soup_message_headers_append (msg->request_headers, - "If-Modified-Since", - value); - value = soup_message_headers_get (entry->headers, "ETag"); - if (value) - soup_message_headers_append (msg->request_headers, - "If-None-Match", - value); - return msg; -} - -#define WEBKIT_SOUP_CACHE_FILE "soup.cache" - -#define WEBKIT_SOUP_CACHE_HEADERS_FORMAT "{ss}" -#define WEBKIT_SOUP_CACHE_PHEADERS_FORMAT "(ssbuuuuua" WEBKIT_SOUP_CACHE_HEADERS_FORMAT ")" -#define WEBKIT_SOUP_CACHE_ENTRIES_FORMAT "a" WEBKIT_SOUP_CACHE_PHEADERS_FORMAT - -/* Basically the same format than above except that some strings are - prepended with &. This way the GVariant returns a pointer to the - data instead of duplicating the string */ -#define WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT "{&s&s}" - -static void -pack_entry (gpointer data, - gpointer user_data) -{ - WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data; - SoupMessageHeadersIter iter; - const gchar *header_key, *header_value; - GVariantBuilder *headers_builder; - GVariantBuilder *entries_builder = (GVariantBuilder *)user_data; - - /* Do not store non-consolidated entries */ - if (entry->dirty || entry->writing || !entry->key) - return; - - /* Pack headers */ - headers_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); - soup_message_headers_iter_init (&iter, entry->headers); - while (soup_message_headers_iter_next (&iter, &header_key, &header_value)) { - if (g_utf8_validate (header_value, -1, NULL)) - g_variant_builder_add (headers_builder, WEBKIT_SOUP_CACHE_HEADERS_FORMAT, - header_key, header_value); - } - - /* Entry data */ - g_variant_builder_add (entries_builder, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT, - entry->key, entry->filename, entry->must_revalidate, - entry->freshness_lifetime, entry->corrected_initial_age, - entry->response_time, entry->hits, entry->length, headers_builder); - - g_variant_builder_unref (headers_builder); -} - -void -webkit_soup_cache_dump (WebKitSoupCache *cache) -{ - WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache); - gchar *filename; - GVariantBuilder *entries_builder; - GVariant *cache_variant; - - if (!g_list_length (cache->priv->lru_start)) - return; - - /* Create the builder and iterate over all entries */ - entries_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); - g_list_foreach (cache->priv->lru_start, pack_entry, entries_builder); - - /* Serialize and dump */ - cache_variant = g_variant_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, entries_builder); - g_variant_builder_unref (entries_builder); - - filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL); - g_file_set_contents (filename, (const gchar *)g_variant_get_data (cache_variant), - g_variant_get_size (cache_variant), NULL); - g_free (filename); - g_variant_unref (cache_variant); -} - -void -webkit_soup_cache_load (WebKitSoupCache *cache) -{ - gchar *filename = NULL, *contents = NULL; - GVariant *cache_variant; - GVariantIter *entries_iter, *headers_iter; - GVariantType *variant_format; - gsize length; - WebKitSoupCacheEntry *entry; - WebKitSoupCachePrivate *priv = cache->priv; - - filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL); - if (!g_file_get_contents (filename, &contents, &length, NULL)) { - g_free (filename); - g_free (contents); - return; - } - g_free (filename); - - variant_format = g_variant_type_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT); - cache_variant = g_variant_new_from_data (variant_format, (const gchar *)contents, length, FALSE, g_free, contents); - g_variant_type_free (variant_format); - - g_variant_get (cache_variant, WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, &entries_iter); - entry = g_slice_new0 (WebKitSoupCacheEntry); - - while (g_variant_iter_loop (entries_iter, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT, - &entry->key, &entry->filename, &entry->must_revalidate, - &entry->freshness_lifetime, &entry->corrected_initial_age, - &entry->response_time, &entry->hits, &entry->length, - &headers_iter)) { - const gchar *header_key, *header_value; - - /* SoupMessage Headers */ - entry->headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); - while (g_variant_iter_loop (headers_iter, WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT, &header_key, &header_value)) - soup_message_headers_append (entry->headers, header_key, header_value); - - /* Insert in cache */ - if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry, FALSE)) - webkit_soup_cache_entry_free (entry, TRUE); - - /* New entry for the next iteration. This creates an - extra object the last iteration but it's worth it - as we save several if's */ - entry = g_slice_new0 (WebKitSoupCacheEntry); - } - /* Remove last created entry */ - g_slice_free (WebKitSoupCacheEntry, entry); - - /* Sort LRU (shouldn't be needed). First reverse as elements - * are always prepended when inserting - */ - cache->priv->lru_start = g_list_reverse (cache->priv->lru_start); - cache->priv->lru_start = g_list_sort (cache->priv->lru_start, lru_compare_func); - - /* frees */ - g_variant_iter_free (entries_iter); - g_variant_unref (cache_variant); -} - -void -webkit_soup_cache_set_max_size (WebKitSoupCache *cache, - guint max_size) -{ - cache->priv->max_size = max_size; - cache->priv->max_entry_data_size = cache->priv->max_size / MAX_ENTRY_DATA_PERCENTAGE; -} - -guint -webkit_soup_cache_get_max_size (WebKitSoupCache *cache) -{ - return cache->priv->max_size; -} diff --git a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.h b/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.h deleted file mode 100644 index a926f98..0000000 --- a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.h +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-cache.h: - * - * Copyright (C) 2009, 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_CACHE_H -#define WEBKIT_SOUP_CACHE_H 1 - -#ifdef G_OS_WIN32 - #ifdef BUILDING_WEBKIT - #define WEBKIT_API __declspec(dllexport) - #else - #define WEBKIT_API __declspec(dllimport) - #endif - #define WEBKIT_OBSOLETE_API WEBKIT_API -#else - #define WEBKIT_API __attribute__((visibility("default"))) - #define WEBKIT_OBSOLETE_API WEBKIT_API __attribute__((deprecated)) -#endif - -#include <libsoup/soup-types.h> -#include <gio/gio.h> - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_CACHE (webkit_soup_cache_get_type ()) -#define WEBKIT_SOUP_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCache)) -#define WEBKIT_SOUP_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCacheClass)) -#define WEBKIT_IS_SOUP_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_CACHE)) -#define WEBKIT_IS_SOUP_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_CACHE)) -#define WEBKIT_SOUP_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCacheClass)) - -typedef struct _WebKitSoupCache WebKitSoupCache; -typedef struct _WebKitSoupCachePrivate WebKitSoupCachePrivate; - -typedef enum { - WEBKIT_SOUP_CACHE_CACHEABLE = (1 << 0), - WEBKIT_SOUP_CACHE_UNCACHEABLE = (1 << 1), - WEBKIT_SOUP_CACHE_INVALIDATES = (1 << 2), - WEBKIT_SOUP_CACHE_VALIDATES = (1 << 3) -} WebKitSoupCacheability; - -typedef enum { - WEBKIT_SOUP_CACHE_RESPONSE_FRESH, - WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION, - WEBKIT_SOUP_CACHE_RESPONSE_STALE -} WebKitSoupCacheResponse; - -typedef enum { - WEBKIT_SOUP_CACHE_SINGLE_USER, - WEBKIT_SOUP_CACHE_SHARED -} WebKitSoupCacheType; - -struct _WebKitSoupCache { - GObject parent_instance; - - WebKitSoupCachePrivate *priv; -}; - -typedef struct { - GObjectClass parent_class; - - /* methods */ - WebKitSoupCacheability (*get_cacheability)(WebKitSoupCache *cache, SoupMessage *msg); - - /* Padding for future expansion */ - void (*_libsoup_reserved1)(void); - void (*_libsoup_reserved2)(void); - void (*_libsoup_reserved3)(void); -} WebKitSoupCacheClass; - -WEBKIT_API GType webkit_soup_cache_get_type (void); -WEBKIT_API WebKitSoupCache *webkit_soup_cache_new (const char *cache_dir, - WebKitSoupCacheType cache_type); -WEBKIT_API void webkit_soup_cache_flush (WebKitSoupCache *cache); -WEBKIT_API void webkit_soup_cache_clear (WebKitSoupCache *cache); - -WEBKIT_API void webkit_soup_cache_dump (WebKitSoupCache *cache); -WEBKIT_API void webkit_soup_cache_load (WebKitSoupCache *cache); - -WEBKIT_API void webkit_soup_cache_set_max_size (WebKitSoupCache *cache, - guint max_size); -WEBKIT_API guint webkit_soup_cache_get_max_size (WebKitSoupCache *cache); - -G_END_DECLS - - -#endif /* WEBKIT_SOUP_CACHE_H */ - diff --git a/Source/WebCore/platform/network/win/DownloadBundleWin.cpp b/Source/WebCore/platform/network/win/DownloadBundleWin.cpp new file mode 100644 index 0000000..f0f3027 --- /dev/null +++ b/Source/WebCore/platform/network/win/DownloadBundleWin.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2011 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 "DownloadBundle.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <io.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +namespace DownloadBundle { + +static UInt32 magicNumber() +{ + return 0xDECAF4EA; +} + +const String& fileExtension() +{ + DEFINE_STATIC_LOCAL(const String, extension, (".download")); + return extension; +} + +bool appendResumeData(CFDataRef resumeData, const String& bundlePath) +{ + if (!resumeData) { + LOG_ERROR("Invalid resume data to write to bundle path"); + return false; + } + if (bundlePath.isEmpty()) { + LOG_ERROR("Cannot write resume data to empty download bundle path"); + return false; + } + + String nullifiedPath = bundlePath; + FILE* bundle = 0; + if (_wfopen_s(&bundle, nullifiedPath.charactersWithNullTermination(), TEXT("ab")) || !bundle) { + LOG_ERROR("Failed to open file %s to append resume data", bundlePath.ascii().data()); + return false; + } + + bool result = false; + + const UInt8* resumeBytes = CFDataGetBytePtr(resumeData); + ASSERT(resumeBytes); + if (!resumeBytes) + goto exit; + + CFIndex resumeLength = CFDataGetLength(resumeData); + ASSERT(resumeLength > 0); + if (resumeLength < 1) + goto exit; + + if (fwrite(resumeBytes, 1, resumeLength, bundle) != resumeLength) { + LOG_ERROR("Failed to write resume data to the bundle - errno(%i)", errno); + goto exit; + } + + if (fwrite(&resumeLength, 4, 1, bundle) != 1) { + LOG_ERROR("Failed to write footer length to the bundle - errno(%i)", errno); + goto exit; + } + + const UInt32& magic = magicNumber(); + if (fwrite(&magic, 4, 1, bundle) != 1) { + LOG_ERROR("Failed to write footer magic number to the bundle - errno(%i)", errno); + goto exit; + } + + result = true; +exit: + fclose(bundle); + return result; +} + +CFDataRef extractResumeData(const String& bundlePath) +{ + if (bundlePath.isEmpty()) { + LOG_ERROR("Cannot create resume data from empty download bundle path"); + return 0; + } + + // Open a handle to the bundle file + String nullifiedPath = bundlePath; + FILE* bundle = 0; + if (_wfopen_s(&bundle, nullifiedPath.charactersWithNullTermination(), TEXT("r+b")) || !bundle) { + LOG_ERROR("Failed to open file %s to get resume data", bundlePath.ascii().data()); + return 0; + } + + CFDataRef result = 0; + Vector<UInt8> footerBuffer; + + // Stat the file to get its size + struct _stat64 fileStat; + if (_fstat64(_fileno(bundle), &fileStat)) + goto exit; + + // Check for the bundle magic number at the end of the file + fpos_t footerMagicNumberPosition = fileStat.st_size - 4; + ASSERT(footerMagicNumberPosition >= 0); + if (footerMagicNumberPosition < 0) + goto exit; + if (fsetpos(bundle, &footerMagicNumberPosition)) + goto exit; + + UInt32 footerMagicNumber = 0; + if (fread(&footerMagicNumber, 4, 1, bundle) != 1) { + LOG_ERROR("Failed to read footer magic number from the bundle - errno(%i)", errno); + goto exit; + } + + if (footerMagicNumber != magicNumber()) { + LOG_ERROR("Footer's magic number does not match 0x%X - errno(%i)", magicNumber(), errno); + goto exit; + } + + // Now we're *reasonably* sure this is a .download bundle we actually wrote. + // Get the length of the resume data + fpos_t footerLengthPosition = fileStat.st_size - 8; + ASSERT(footerLengthPosition >= 0); + if (footerLengthPosition < 0) + goto exit; + + if (fsetpos(bundle, &footerLengthPosition)) + goto exit; + + UInt32 footerLength = 0; + if (fread(&footerLength, 4, 1, bundle) != 1) { + LOG_ERROR("Failed to read ResumeData length from the bundle - errno(%i)", errno); + goto exit; + } + + // Make sure theres enough bytes to read in for the resume data, and perform the read + fpos_t footerStartPosition = fileStat.st_size - 8 - footerLength; + ASSERT(footerStartPosition >= 0); + if (footerStartPosition < 0) + goto exit; + if (fsetpos(bundle, &footerStartPosition)) + goto exit; + + footerBuffer.resize(footerLength); + if (fread(footerBuffer.data(), 1, footerLength, bundle) != footerLength) { + LOG_ERROR("Failed to read ResumeData from the bundle - errno(%i)", errno); + goto exit; + } + + // CFURLDownload will seek to the appropriate place in the file (before our footer) and start overwriting from there + // However, say we were within a few hundred bytes of the end of a download when it was paused - + // The additional footer extended the length of the file beyond its final length, and there will be junk data leftover + // at the end. Therefore, now that we've retrieved the footer data, we need to truncate it. + if (errno_t resizeError = _chsize_s(_fileno(bundle), footerStartPosition)) { + LOG_ERROR("Failed to truncate the resume footer off the end of the file - errno(%i)", resizeError); + goto exit; + } + + // Finally, make the resume data. Now, it is possible by some twist of fate the bundle magic number + // was naturally at the end of the file and its not actually a valid bundle. That, or someone engineered + // it that way to try to attack us. In that cause, this CFData will successfully create but when we + // actually try to start the CFURLDownload using this bogus data, it will fail and we will handle that gracefully + result = CFDataCreate(0, footerBuffer.data(), footerLength); +exit: + fclose(bundle); + return result; +} + +} // namespace DownloadBundle + +} // namespace WebCore |