diff options
Diffstat (limited to 'WebCore/platform/network')
24 files changed, 757 insertions, 178 deletions
diff --git a/WebCore/platform/network/ResourceRequestBase.cpp b/WebCore/platform/network/ResourceRequestBase.cpp index 5312007..ae8316a 100644 --- a/WebCore/platform/network/ResourceRequestBase.cpp +++ b/WebCore/platform/network/ResourceRequestBase.cpp @@ -234,6 +234,16 @@ void ResourceRequestBase::setHTTPHeaderField(const char* name, const String& val setHTTPHeaderField(AtomicString(name), value); } +void ResourceRequestBase::clearHTTPAuthorization() +{ + updateResourceRequest(); + + m_httpHeaderFields.remove("Authorization"); + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + void ResourceRequestBase::clearHTTPReferrer() { updateResourceRequest(); diff --git a/WebCore/platform/network/ResourceRequestBase.h b/WebCore/platform/network/ResourceRequestBase.h index 33a184e..5cb7ee3 100644 --- a/WebCore/platform/network/ResourceRequestBase.h +++ b/WebCore/platform/network/ResourceRequestBase.h @@ -100,6 +100,8 @@ namespace WebCore { void addHTTPHeaderField(const AtomicString& name, const String& value); void addHTTPHeaderFields(const HTTPHeaderMap& headerFields); + void clearHTTPAuthorization(); + String httpContentType() const { return httpHeaderField("Content-Type"); } void setHTTPContentType(const String& httpContentType) { setHTTPHeaderField("Content-Type", httpContentType); } diff --git a/WebCore/platform/network/SocketStreamErrorBase.cpp b/WebCore/platform/network/SocketStreamErrorBase.cpp index 72fb44c..bbb5d55 100644 --- a/WebCore/platform/network/SocketStreamErrorBase.cpp +++ b/WebCore/platform/network/SocketStreamErrorBase.cpp @@ -42,7 +42,22 @@ SocketStreamError SocketStreamErrorBase::copy() const bool SocketStreamErrorBase::compare(const SocketStreamError& a, const SocketStreamError& b) { - return a.errorCode() == b.errorCode(); + if (a.isNull() && b.isNull()) + return true; + + if (a.isNull() || b.isNull()) + return false; + + if (a.errorCode() != b.errorCode()) + return false; + + if (a.failingURL() != b.failingURL()) + return false; + + if (a.localizedDescription() != b.localizedDescription()) + return false; + + return true; } } // namespace WebCore diff --git a/WebCore/platform/network/SocketStreamErrorBase.h b/WebCore/platform/network/SocketStreamErrorBase.h index b7ca35b..e6cc567 100644 --- a/WebCore/platform/network/SocketStreamErrorBase.h +++ b/WebCore/platform/network/SocketStreamErrorBase.h @@ -32,6 +32,8 @@ #ifndef SocketStreamErrorBase_h #define SocketStreamErrorBase_h +#include "PlatformString.h" + namespace WebCore { class SocketStreamError; @@ -44,6 +46,8 @@ namespace WebCore { bool isNull() const { return m_isNull; } int errorCode() const { return m_errorCode; } + const String& failingURL() const { return m_failingURL; } + const String& localizedDescription() const { return m_localizedDescription; } static bool compare(const SocketStreamError&, const SocketStreamError&); @@ -60,7 +64,17 @@ namespace WebCore { { } + SocketStreamErrorBase(int errorCode, const String& failingURL, const String& localizedDescription) + : m_errorCode(errorCode) + , m_failingURL(failingURL) + , m_localizedDescription(localizedDescription) + , m_isNull(false) + { + } + int m_errorCode; + String m_failingURL; + String m_localizedDescription; bool m_isNull; }; diff --git a/WebCore/platform/network/cf/DNSCFNet.cpp b/WebCore/platform/network/cf/DNSCFNet.cpp index fbceb7d..166abbf 100644 --- a/WebCore/platform/network/cf/DNSCFNet.cpp +++ b/WebCore/platform/network/cf/DNSCFNet.cpp @@ -27,6 +27,7 @@ #include "config.h" #include "DNS.h" +#include "KURL.h" #include "Timer.h" #include <wtf/HashSet.h> #include <wtf/RetainPtr.h> @@ -37,6 +38,10 @@ #include "LoaderRunLoopCF.h" #endif +#if defined(BUILDING_ON_LEOPARD) +#include <SystemConfiguration/SystemConfiguration.h> +#endif + #ifdef BUILDING_ON_TIGER // This function is available on Tiger, but not declared in the CFRunLoop.h header on Tiger. extern "C" CFRunLoopRef CFRunLoopGetMain(); @@ -62,6 +67,37 @@ const int maxRequestsToQueue = 64; // If there were queued names that couldn't be sent simultaneously, check the state of resolvers after this delay. const double retryResolvingInSeconds = 0.1; +static bool proxyIsEnabledInSystemPreferences() +{ + // Don't do DNS prefetch if proxies are involved. For many proxy types, the user agent is never exposed + // to the IP address during normal operation. Querying an internal DNS server may not help performance, + // as it doesn't necessarily look up the actual external IP. Also, if DNS returns a fake internal address, + // local caches may keep it even after re-connecting to another network. + +#if !defined(BUILDING_ON_LEOPARD) + RetainPtr<CFDictionaryRef> proxySettings(AdoptCF, CFNetworkCopySystemProxySettings()); +#else + RetainPtr<CFDictionaryRef> proxySettings(AdoptCF, SCDynamicStoreCopyProxies(0)); +#endif + if (!proxySettings) + return false; + + static CFURLRef httpCFURL = KURL(ParsedURLString, "http://example.com/").createCFURL(); + static CFURLRef httpsCFURL = KURL(ParsedURLString, "https://example.com/").createCFURL(); + + RetainPtr<CFArrayRef> httpProxyArray(AdoptCF, CFNetworkCopyProxiesForURL(httpCFURL, proxySettings.get())); + RetainPtr<CFArrayRef> httpsProxyArray(AdoptCF, CFNetworkCopyProxiesForURL(httpsCFURL, proxySettings.get())); + + CFIndex httpProxyCount = CFArrayGetCount(httpProxyArray.get()); + CFIndex httpsProxyCount = CFArrayGetCount(httpsProxyArray.get()); + if (httpProxyCount == 1 && CFEqual(CFDictionaryGetValue(static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(httpProxyArray.get(), 0)), kCFProxyTypeKey), kCFProxyTypeNone)) + httpProxyCount = 0; + if (httpsProxyCount == 1 && CFEqual(CFDictionaryGetValue(static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(httpsProxyArray.get(), 0)), kCFProxyTypeKey), kCFProxyTypeNone)) + httpsProxyCount = 0; + + return httpProxyCount || httpsProxyCount; +} + class DNSResolveQueue : public TimerBase { public: static DNSResolveQueue& shared(); @@ -92,6 +128,9 @@ void DNSResolveQueue::add(const String& name) { // If there are no names queued, and few enough are in flight, resolve immediately (the mouse may be over a link). if (!m_names.size()) { + if (proxyIsEnabledInSystemPreferences()) + return; + if (atomicIncrement(&m_requestsInFlight) <= namesToResolveImmediately) { resolve(name); return; @@ -115,6 +154,11 @@ void DNSResolveQueue::decrementRequestCount() void DNSResolveQueue::fired() { + if (proxyIsEnabledInSystemPreferences()) { + m_names.clear(); + return; + } + int requestsAllowed = maxSimultaneousRequests - m_requestsInFlight; for (; !m_names.isEmpty() && requestsAllowed > 0; --requestsAllowed) { diff --git a/WebCore/platform/network/cf/ResourceHandleCFNet.cpp b/WebCore/platform/network/cf/ResourceHandleCFNet.cpp index e48bd2d..f0773d2 100644 --- a/WebCore/platform/network/cf/ResourceHandleCFNet.cpp +++ b/WebCore/platform/network/cf/ResourceHandleCFNet.cpp @@ -481,6 +481,8 @@ void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceRes d->m_pass = url.pass(); d->m_lastHTTPMethod = request.httpMethod(); request.removeCredentials(); + if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) + request.clearHTTPAuthorization(); client()->willSendRequest(this, request, redirectResponse); } diff --git a/WebCore/platform/network/cf/ResourceResponseCFNet.cpp b/WebCore/platform/network/cf/ResourceResponseCFNet.cpp index 469e5ad..9a83add 100644 --- a/WebCore/platform/network/cf/ResourceResponseCFNet.cpp +++ b/WebCore/platform/network/cf/ResourceResponseCFNet.cpp @@ -79,6 +79,11 @@ void ResourceResponse::platformLazyInit() m_expectedContentLength = CFURLResponseGetExpectedContentLength(m_cfResponse.get()); m_textEncodingName = CFURLResponseGetTextEncodingName(m_cfResponse.get()); + // Workaround for <rdar://problem/8757088>, can be removed once that is fixed. + unsigned textEncodingNameLength = m_textEncodingName.length(); + if (textEncodingNameLength >= 2 && m_textEncodingName[0U] == '"' && m_textEncodingName[textEncodingNameLength - 1] == '"') + m_textEncodingName = m_textEncodingName.substring(1, textEncodingNameLength - 2); + m_lastModifiedDate = toTimeT(CFURLResponseGetLastModifiedDate(m_cfResponse.get())); RetainPtr<CFStringRef> suggestedFilename(AdoptCF, CFURLResponseCopySuggestedFilename(m_cfResponse.get())); diff --git a/WebCore/platform/network/cf/SocketStreamError.h b/WebCore/platform/network/cf/SocketStreamError.h index 6a0b441..5a0ca07 100644 --- a/WebCore/platform/network/cf/SocketStreamError.h +++ b/WebCore/platform/network/cf/SocketStreamError.h @@ -42,7 +42,10 @@ public: : SocketStreamErrorBase(errorCode) { } - + SocketStreamError(int errorCode, const String& failingURL, const String& localizedDescription) + : SocketStreamErrorBase(errorCode, failingURL, localizedDescription) + { + } }; } // namespace WebCore diff --git a/WebCore/platform/network/cf/SocketStreamHandle.h b/WebCore/platform/network/cf/SocketStreamHandle.h index 41d543a..df1d4a3 100644 --- a/WebCore/platform/network/cf/SocketStreamHandle.h +++ b/WebCore/platform/network/cf/SocketStreamHandle.h @@ -84,6 +84,10 @@ private: void readStreamCallback(CFStreamEventType); void writeStreamCallback(CFStreamEventType); +#ifndef BUILDING_ON_TIGER + void reportErrorToClient(CFErrorRef); +#endif + // No authentication for streams per se, but proxy may ask for credentials. virtual void receivedCredential(const AuthenticationChallenge&, const Credential&); virtual void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&); diff --git a/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp b/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp index 24b5835..821b1ca 100644 --- a/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp +++ b/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp @@ -39,6 +39,7 @@ #include "SocketStreamError.h" #include "SocketStreamHandleClient.h" #include <wtf/MainThread.h> +#include <wtf/text/StringConcatenate.h> #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) #include <SystemConfiguration/SystemConfiguration.h> @@ -530,8 +531,13 @@ void SocketStreamHandle::readStreamCallback(CFStreamEventType type) ASSERT_NOT_REACHED(); break; case kCFStreamEventErrorOccurred: { +#ifndef BUILDING_ON_TIGER + RetainPtr<CFErrorRef> error(AdoptCF, CFReadStreamCopyError(m_readStream.get())); + reportErrorToClient(error.get()); +#else CFStreamError error = CFReadStreamGetError(m_readStream.get()); m_client->didFail(this, SocketStreamError(error.error)); // FIXME: Provide a sensible error. +#endif break; } case kCFStreamEventEndEncountered: @@ -574,8 +580,13 @@ void SocketStreamHandle::writeStreamCallback(CFStreamEventType type) break; } case kCFStreamEventErrorOccurred: { +#ifndef BUILDING_ON_TIGER + RetainPtr<CFErrorRef> error(AdoptCF, CFWriteStreamCopyError(m_writeStream.get())); + reportErrorToClient(error.get()); +#else CFStreamError error = CFWriteStreamGetError(m_writeStream.get()); m_client->didFail(this, SocketStreamError(error.error)); // FIXME: Provide a sensible error. +#endif break; } case kCFStreamEventEndEncountered: @@ -584,6 +595,29 @@ void SocketStreamHandle::writeStreamCallback(CFStreamEventType type) } } +#ifndef BUILDING_ON_TIGER +void SocketStreamHandle::reportErrorToClient(CFErrorRef error) +{ + CFIndex errorCode = CFErrorGetCode(error); + String description; + +#if PLATFORM(MAC) + if (CFEqual(CFErrorGetDomain(error), kCFErrorDomainOSStatus)) { + const char* descriptionOSStatus = GetMacOSStatusCommentString(static_cast<OSStatus>(errorCode)); + if (descriptionOSStatus && descriptionOSStatus[0] != '\0') + description = makeString("OSStatus Error ", String::number(errorCode), ": ", descriptionOSStatus); + } +#endif + + if (description.isNull()) { + RetainPtr<CFStringRef> descriptionCF(AdoptCF, CFErrorCopyDescription(error)); + description = String(descriptionCF.get()); + } + + m_client->didFail(this, SocketStreamError(static_cast<int>(errorCode), m_url.string(), description)); +} +#endif // BUILDING_ON_TIGER + SocketStreamHandle::~SocketStreamHandle() { LOG(Network, "SocketStreamHandle %p dtor", this); diff --git a/WebCore/platform/network/mac/AuthenticationChallenge.h b/WebCore/platform/network/mac/AuthenticationChallenge.h index d74a92c..8f60933 100644 --- a/WebCore/platform/network/mac/AuthenticationChallenge.h +++ b/WebCore/platform/network/mac/AuthenticationChallenge.h @@ -49,6 +49,7 @@ public: NSURLAuthenticationChallenge *nsURLAuthenticationChallenge() const { return m_nsChallenge.get(); } void setAuthenticationClient(AuthenticationClient*); // Changes sender to one that invokes client methods. + AuthenticationClient* authenticationClient() const; private: friend class AuthenticationChallengeBase; diff --git a/WebCore/platform/network/mac/AuthenticationMac.mm b/WebCore/platform/network/mac/AuthenticationMac.mm index 1c05917..cdf643f 100644 --- a/WebCore/platform/network/mac/AuthenticationMac.mm +++ b/WebCore/platform/network/mac/AuthenticationMac.mm @@ -41,6 +41,7 @@ using namespace WebCore; AuthenticationClient* m_client; } - (id)initWithAuthenticationClient:(AuthenticationClient*)client; +- (AuthenticationClient*)client; - (void)detachClient; @end @@ -55,6 +56,11 @@ using namespace WebCore; return self; } +- (AuthenticationClient*)client +{ + return m_client; +} + - (void)detachClient { m_client = 0; @@ -123,6 +129,14 @@ void AuthenticationChallenge::setAuthenticationClient(AuthenticationClient* clie } } +AuthenticationClient* AuthenticationChallenge::authenticationClient() const +{ + if ([m_sender.get() isMemberOfClass:[WebCoreAuthenticationClientAsChallengeSender class]]) + return [static_cast<WebCoreAuthenticationClientAsChallengeSender*>(m_sender.get()) client]; + + return 0; +} + bool AuthenticationChallenge::platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b) { if (a.sender() != b.sender()) diff --git a/WebCore/platform/network/mac/ResourceHandleMac.mm b/WebCore/platform/network/mac/ResourceHandleMac.mm index daec366..caa33d7 100644 --- a/WebCore/platform/network/mac/ResourceHandleMac.mm +++ b/WebCore/platform/network/mac/ResourceHandleMac.mm @@ -552,6 +552,8 @@ void ResourceHandle::willSendRequest(ResourceRequest& request, const ResourceRes d->m_pass = url.pass(); d->m_lastHTTPMethod = request.httpMethod(); request.removeCredentials(); + if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) + request.clearHTTPAuthorization(); client()->willSendRequest(this, request, redirectResponse); } diff --git a/WebCore/platform/network/mac/ResourceResponseMac.mm b/WebCore/platform/network/mac/ResourceResponseMac.mm index e1f1790..4376b64 100644 --- a/WebCore/platform/network/mac/ResourceResponseMac.mm +++ b/WebCore/platform/network/mac/ResourceResponseMac.mm @@ -74,6 +74,12 @@ void ResourceResponse::platformLazyInit() m_mimeType = [m_nsResponse.get() MIMEType]; m_expectedContentLength = [m_nsResponse.get() expectedContentLength]; m_textEncodingName = [m_nsResponse.get() textEncodingName]; + + // Workaround for <rdar://problem/8757088>, can be removed once that is fixed. + unsigned textEncodingNameLength = m_textEncodingName.length(); + if (textEncodingNameLength >= 2 && m_textEncodingName[0U] == '"' && m_textEncodingName[textEncodingNameLength - 1] == '"') + m_textEncodingName = m_textEncodingName.substring(1, textEncodingNameLength - 2); + m_suggestedFilename = [m_nsResponse.get() suggestedFilename]; if ([m_nsResponse.get() isKindOfClass:[NSHTTPURLResponse class]]) { diff --git a/WebCore/platform/network/qt/AuthenticationChallenge.h b/WebCore/platform/network/qt/AuthenticationChallenge.h index 753ac6f..ebbc0cd 100644 --- a/WebCore/platform/network/qt/AuthenticationChallenge.h +++ b/WebCore/platform/network/qt/AuthenticationChallenge.h @@ -29,6 +29,8 @@ namespace WebCore { +class AuthenticationClient; + class AuthenticationChallenge : public AuthenticationChallengeBase { public: AuthenticationChallenge() @@ -39,6 +41,9 @@ public: : AuthenticationChallengeBase(protectionSpace, proposedCredential, previousFailureCount, response, error) { } + + AuthenticationClient* authenticationClient() const { return 0; } // FIXME: Implement! + }; } diff --git a/WebCore/platform/network/qt/QNetworkReplyHandler.cpp b/WebCore/platform/network/qt/QNetworkReplyHandler.cpp index 01e624e..2ff7d9c 100644 --- a/WebCore/platform/network/qt/QNetworkReplyHandler.cpp +++ b/WebCore/platform/network/qt/QNetworkReplyHandler.cpp @@ -23,6 +23,7 @@ #include "HTTPParsers.h" #include "MIMETypeRegistry.h" +#include "QtNAMThreadSafeProxy.h" #include "ResourceHandle.h" #include "ResourceHandleClient.h" #include "ResourceHandleInternal.h" @@ -46,7 +47,7 @@ // It is fixed in Qt 4.6.3. See https://bugs.webkit.org/show_bug.cgi?id=32113 // and https://bugs.webkit.org/show_bug.cgi?id=36755 #if QT_VERSION > QT_VERSION_CHECK(4, 6, 2) -#define SIGNAL_CONN Qt::DirectConnection +#define SIGNAL_CONN Qt::AutoConnection #else #define SIGNAL_CONN Qt::QueuedConnection #endif @@ -56,8 +57,9 @@ static const int gMaxRecursionLimit = 10; namespace WebCore { // Take a deep copy of the FormDataElement -FormDataIODevice::FormDataIODevice(FormData* data) - : m_formElements(data ? data->elements() : Vector<FormDataElement>()) +FormDataIODevice::FormDataIODevice(FormData* data, QObject* parent) + : QIODevice(parent) + , m_formElements(data ? data->elements() : Vector<FormDataElement>()) , m_currentFile(0) , m_currentDelta(0) , m_fileSize(0) @@ -184,7 +186,7 @@ QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadMode load , m_resourceHandle(handle) , m_redirected(false) , m_responseSent(false) - , m_responseDataSent(false) + , m_responseContainsData(false) , m_loadMode(loadMode) , m_shouldStart(true) , m_shouldFinish(false) @@ -192,6 +194,9 @@ QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadMode load , m_shouldForwardData(false) , m_redirectionTries(gMaxRecursionLimit) { + // Make this a direct function call once we require 4.6.1+. + connect(this, SIGNAL(processQueuedItems()), this, SLOT(sendQueuedItems()), SIGNAL_CONN); + const ResourceRequest &r = m_resourceHandle->firstRequest(); if (r.httpMethod() == "GET") @@ -222,6 +227,12 @@ QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadMode load start(); } +QNetworkReplyHandler::~QNetworkReplyHandler() +{ + if (m_reply) + m_reply->deleteLater(); +} + void QNetworkReplyHandler::setLoadMode(LoadMode mode) { // https://bugs.webkit.org/show_bug.cgi?id=26556 @@ -231,9 +242,13 @@ void QNetworkReplyHandler::setLoadMode(LoadMode mode) case LoadNormal: m_loadMode = LoadResuming; emit processQueuedItems(); + // Restart forwarding only after processQueuedItems to make sure + // our buffered data was handled before any incoming data. + m_reply->setForwardingDefered(false); break; case LoadDeferred: m_loadMode = LoadDeferred; + m_reply->setForwardingDefered(true); break; case LoadResuming: Q_ASSERT(0); // should never happen @@ -245,31 +260,30 @@ void QNetworkReplyHandler::abort() { m_resourceHandle = 0; if (m_reply) { - QNetworkReply* reply = release(); + QtNetworkReplyThreadSafeProxy* reply = release(); reply->abort(); reply->deleteLater(); } deleteLater(); } -QNetworkReply* QNetworkReplyHandler::release() +QtNetworkReplyThreadSafeProxy* QNetworkReplyHandler::release() { - QNetworkReply* reply = m_reply; + QtNetworkReplyThreadSafeProxy* reply = m_reply; if (m_reply) { disconnect(m_reply, 0, this, 0); // We have queued connections to the QNetworkReply. Make sure any // posted meta call events that were the result of a signal emission // don't reach the slots in our instance. QCoreApplication::removePostedEvents(this, QEvent::MetaCall); - m_reply->setParent(0); m_reply = 0; } return reply; } -static bool ignoreHttpError(QNetworkReply* reply, bool receivedData) +static bool ignoreHttpError(QtNetworkReplyThreadSafeProxy* reply, bool receivedData) { - int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + int httpStatusCode = reply->httpStatusCode(); if (httpStatusCode == 401 || httpStatusCode == 407) return true; @@ -300,29 +314,33 @@ void QNetworkReplyHandler::finish() return; } - QNetworkReply* oldReply = m_reply; + if (!m_redirected) { + if (!m_reply->error() || ignoreHttpError(m_reply, m_responseContainsData)) + client->didFinishLoading(m_resourceHandle, 0); + else { + QUrl url = m_reply->url(); + int httpStatusCode = m_reply->httpStatusCode(); - if (m_redirected) { - resetState(); - start(); - } else if (!m_reply->error() || ignoreHttpError(m_reply, m_responseDataSent)) { - client->didFinishLoading(m_resourceHandle, 0); + if (httpStatusCode) { + ResourceError error("HTTP", httpStatusCode, url.toString(), QString::fromAscii(m_reply->httpReasonPhrase())); + client->didFail(m_resourceHandle, error); + } else { + ResourceError error("QtNetwork", m_reply->error(), url.toString(), m_reply->errorString()); + client->didFail(m_resourceHandle, error); + } + } + if (m_reply) { + m_reply->deleteLater(); + m_reply = 0; + } } else { - QUrl url = m_reply->url(); - int httpStatusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (httpStatusCode) { - ResourceError error("HTTP", httpStatusCode, url.toString(), m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString()); - client->didFail(m_resourceHandle, error); - } else { - ResourceError error("QtNetwork", m_reply->error(), url.toString(), m_reply->errorString()); - client->didFail(m_resourceHandle, error); + if (m_reply) { + m_reply->deleteLater(); + m_reply = 0; } + resetState(); + start(); } - - oldReply->deleteLater(); - if (oldReply == m_reply) - m_reply = 0; } void QNetworkReplyHandler::sendResponseIfNeeded() @@ -331,7 +349,7 @@ void QNetworkReplyHandler::sendResponseIfNeeded() if (m_shouldSendResponse) return; - if (m_reply->error() && !ignoreHttpError(m_reply, m_responseDataSent)) + if (m_reply->error() && !ignoreHttpError(m_reply, m_responseContainsData)) return; if (m_responseSent || !m_resourceHandle) @@ -342,7 +360,7 @@ void QNetworkReplyHandler::sendResponseIfNeeded() if (!client) return; - WTF::String contentType = m_reply->header(QNetworkRequest::ContentTypeHeader).toString(); + WTF::String contentType = m_reply->contentTypeHeader(); WTF::String encoding = extractCharsetFromMediaType(contentType); WTF::String mimeType = extractMIMETypeFromMediaType(contentType); @@ -353,7 +371,7 @@ void QNetworkReplyHandler::sendResponseIfNeeded() KURL url(m_reply->url()); ResourceResponse response(url, mimeType.lower(), - m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), + m_reply->contentLengthHeader(), encoding, String()); if (url.isLocalFile()) { @@ -362,10 +380,10 @@ void QNetworkReplyHandler::sendResponseIfNeeded() } // The status code is equal to 0 for protocols not in the HTTP family. - int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + int statusCode = m_reply->httpStatusCode(); if (url.protocolInHTTPFamily()) { - String suggestedFilename = filenameFromHTTPContentDisposition(QString::fromAscii(m_reply->rawHeader("Content-Disposition"))); + String suggestedFilename = filenameFromHTTPContentDisposition(QString::fromAscii(m_reply->contentDispositionHeader())); if (!suggestedFilename.isEmpty()) response.setSuggestedFilename(suggestedFilename); @@ -373,21 +391,15 @@ void QNetworkReplyHandler::sendResponseIfNeeded() response.setSuggestedFilename(url.lastPathComponent()); response.setHTTPStatusCode(statusCode); - response.setHTTPStatusText(m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray().constData()); + response.setHTTPStatusText(m_reply->httpReasonPhrase().constData()); // Add remaining headers. -#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) - foreach (const QNetworkReply::RawHeaderPair& pair, m_reply->rawHeaderPairs()) { + foreach (const QtNetworkReplyThreadSafeProxy::RawHeaderPair& pair, m_reply->rawHeaderPairs()) { response.setHTTPHeaderField(QString::fromAscii(pair.first), QString::fromAscii(pair.second)); } -#else - foreach (const QByteArray& headerName, m_reply->rawHeaderList()) { - response.setHTTPHeaderField(QString::fromAscii(headerName), QString::fromAscii(m_reply->rawHeader(headerName))); - } -#endif } - QUrl redirection = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + QUrl redirection = m_reply->redirectionTarget(); if (redirection.isValid()) { QUrl newUrl = m_reply->url().resolved(redirection); @@ -432,11 +444,16 @@ void QNetworkReplyHandler::sendResponseIfNeeded() client->didReceiveResponse(m_resourceHandle, response); } -void QNetworkReplyHandler::forwardData() +void QNetworkReplyHandler::forwardData(const QByteArray &data) { m_shouldForwardData = (m_loadMode != LoadNormal); - if (m_shouldForwardData) + if (m_shouldForwardData) { + m_bufferedData += data; return; + } + + if (!data.isEmpty()) + m_responseContainsData = true; sendResponseIfNeeded(); @@ -447,16 +464,12 @@ void QNetworkReplyHandler::forwardData() if (!m_resourceHandle) return; - QByteArray data = m_reply->read(m_reply->bytesAvailable()); - ResourceHandleClient* client = m_resourceHandle->client(); if (!client) return; - if (!data.isEmpty()) { - m_responseDataSent = true; + if (!data.isEmpty()) client->didReceiveData(m_resourceHandle, data.constData(), data.length(), data.length() /*FixMe*/); - } } void QNetworkReplyHandler::uploadProgress(qint64 bytesSent, qint64 bytesTotal) @@ -493,41 +506,53 @@ void QNetworkReplyHandler::start() && (!url.toLocalFile().isEmpty() || url.scheme() == QLatin1String("data"))) m_method = QNetworkAccessManager::GetOperation; + m_reply = new QtNetworkReplyThreadSafeProxy(manager); + connect(m_reply, SIGNAL(finished()), this, SLOT(finish()), SIGNAL_CONN); + + // For http(s) we know that the headers are complete upon metaDataChanged() emission, so we + // can send the response as early as possible + if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) + connect(m_reply, SIGNAL(metaDataChanged()), this, SLOT(sendResponseIfNeeded()), SIGNAL_CONN); + + connect(m_reply, SIGNAL(dataReceived(const QByteArray&)), this, SLOT(forwardData(const QByteArray&)), SIGNAL_CONN); + + if (m_resourceHandle->firstRequest().reportUploadProgress()) + connect(m_reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64)), SIGNAL_CONN); + switch (m_method) { case QNetworkAccessManager::GetOperation: - m_reply = manager->get(m_request); + m_reply->get(m_request); break; case QNetworkAccessManager::PostOperation: { - FormDataIODevice* postDevice = new FormDataIODevice(d->m_firstRequest.httpBody()); + FormDataIODevice* postDevice = new FormDataIODevice(d->m_firstRequest.httpBody(), this); // We may be uploading files so prevent QNR from buffering data m_request.setHeader(QNetworkRequest::ContentLengthHeader, postDevice->getFormDataSize()); m_request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, QVariant(true)); - m_reply = manager->post(m_request, postDevice); - postDevice->setParent(m_reply); + m_reply->post(m_request, postDevice); break; } case QNetworkAccessManager::HeadOperation: - m_reply = manager->head(m_request); + m_reply->head(m_request); break; case QNetworkAccessManager::PutOperation: { - FormDataIODevice* putDevice = new FormDataIODevice(d->m_firstRequest.httpBody()); + FormDataIODevice* putDevice = new FormDataIODevice(d->m_firstRequest.httpBody(), this); // We may be uploading files so prevent QNR from buffering data m_request.setHeader(QNetworkRequest::ContentLengthHeader, putDevice->getFormDataSize()); m_request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, QVariant(true)); - m_reply = manager->put(m_request, putDevice); - putDevice->setParent(m_reply); + m_reply->put(m_request, putDevice); break; } case QNetworkAccessManager::DeleteOperation: { - m_reply = manager->deleteResource(m_request); + m_reply->deleteResource(m_request); break; } #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) case QNetworkAccessManager::CustomOperation: - m_reply = manager->sendCustomRequest(m_request, m_resourceHandle->firstRequest().httpMethod().latin1().data()); + m_reply->sendCustomRequest(m_request, m_resourceHandle->firstRequest().httpMethod().latin1().data()); break; #endif case QNetworkAccessManager::UnknownOperation: { + m_reply->deleteLater(); m_reply = 0; ResourceHandleClient* client = m_resourceHandle->client(); if (client) { @@ -539,36 +564,13 @@ void QNetworkReplyHandler::start() return; } } - - m_reply->setParent(this); - - connect(m_reply, SIGNAL(finished()), - this, SLOT(finish()), SIGNAL_CONN); - - // For http(s) we know that the headers are complete upon metaDataChanged() emission, so we - // can send the response as early as possible - if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) - connect(m_reply, SIGNAL(metaDataChanged()), - this, SLOT(sendResponseIfNeeded()), SIGNAL_CONN); - - connect(m_reply, SIGNAL(readyRead()), - this, SLOT(forwardData()), SIGNAL_CONN); - - if (m_resourceHandle->firstRequest().reportUploadProgress()) { - connect(m_reply, SIGNAL(uploadProgress(qint64, qint64)), - this, SLOT(uploadProgress(qint64, qint64)), SIGNAL_CONN); - } - - // Make this a direct function call once we require 4.6.1+. - connect(this, SIGNAL(processQueuedItems()), - this, SLOT(sendQueuedItems()), SIGNAL_CONN); } void QNetworkReplyHandler::resetState() { m_redirected = false; m_responseSent = false; - m_responseDataSent = false; + m_responseContainsData = false; m_shouldStart = true; m_shouldFinish = false; m_shouldSendResponse = false; @@ -587,8 +589,10 @@ void QNetworkReplyHandler::sendQueuedItems() if (m_shouldSendResponse) sendResponseIfNeeded(); - if (m_shouldForwardData) - forwardData(); + if (m_shouldForwardData) { + forwardData(m_bufferedData); + m_bufferedData.clear(); + } if (m_shouldFinish) finish(); diff --git a/WebCore/platform/network/qt/QNetworkReplyHandler.h b/WebCore/platform/network/qt/QNetworkReplyHandler.h index 884a1a4..11638b3 100644 --- a/WebCore/platform/network/qt/QNetworkReplyHandler.h +++ b/WebCore/platform/network/qt/QNetworkReplyHandler.h @@ -23,6 +23,7 @@ #include <QNetworkRequest> #include <QNetworkAccessManager> +#include <QNetworkReply> #include "FormData.h" @@ -34,6 +35,7 @@ QT_END_NAMESPACE namespace WebCore { class ResourceHandle; +class QtNetworkReplyThreadSafeProxy; class QNetworkReplyHandler : public QObject { @@ -46,13 +48,12 @@ public: }; QNetworkReplyHandler(ResourceHandle *handle, LoadMode); + ~QNetworkReplyHandler(); void setLoadMode(LoadMode); - QNetworkReply* reply() const { return m_reply; } - void abort(); - QNetworkReply* release(); + QtNetworkReplyThreadSafeProxy* release(); signals: void processQueuedItems(); @@ -60,7 +61,7 @@ signals: private slots: void finish(); void sendResponseIfNeeded(); - void forwardData(); + void forwardData(const QByteArray &data); void sendQueuedItems(); void uploadProgress(qint64 bytesSent, qint64 bytesTotal); @@ -69,11 +70,11 @@ private: void resetState(); String httpMethod() const; - QNetworkReply* m_reply; + QtNetworkReplyThreadSafeProxy* m_reply; ResourceHandle* m_resourceHandle; bool m_redirected; bool m_responseSent; - bool m_responseDataSent; + bool m_responseContainsData; LoadMode m_loadMode; QNetworkAccessManager::Operation m_method; QNetworkRequest m_request; @@ -84,6 +85,7 @@ private: bool m_shouldSendResponse; bool m_shouldForwardData; int m_redirectionTries; + QByteArray m_bufferedData; }; // Self destructing QIODevice for FormData @@ -94,7 +96,7 @@ private: class FormDataIODevice : public QIODevice { Q_OBJECT public: - FormDataIODevice(FormData*); + FormDataIODevice(FormData*, QObject* parent = 0); ~FormDataIODevice(); bool isSequential() const; diff --git a/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp b/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp new file mode 100644 index 0000000..0caeb05 --- /dev/null +++ b/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp @@ -0,0 +1,190 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "config.h" +#include "QtNAMThreadSafeProxy.h" + +#include "Assertions.h" +#include <QAbstractNetworkCache> +#include <QNetworkAccessManager> +#include <QNetworkCookieJar> +#include <QStringList> + +// Use unused variables to be able to call qRegisterMetaType statically. +static int dummyStaticVar1 = qRegisterMetaType<QFutureInterface<bool> >("QFutureInterface<bool>"); +static int dummyStaticVar2 = qRegisterMetaType<QFutureInterface<QList<QNetworkCookie> > >("QFutureInterface<QList<QNetworkCookie> >"); + +namespace WebCore { + +QtNAMThreadSafeProxy::QtNAMThreadSafeProxy(QNetworkAccessManager *manager) + : m_manager(manager) +{ + moveToThread(manager->thread()); + + connect(this, SIGNAL(localSetCookiesRequested(const QUrl&, const QString&)), SLOT(localSetCookies(const QUrl&, const QString&))); + connect(this, SIGNAL(localCookiesForUrlRequested(QFutureInterface<QList<QNetworkCookie> >, const QUrl&)), SLOT(localCookiesForUrl(QFutureInterface<QList<QNetworkCookie> >, const QUrl&))); + connect(this, SIGNAL(localWillLoadFromCacheRequested(QFutureInterface<bool>, const QUrl&)), SLOT(localWillLoadFromCache(QFutureInterface<bool>, const QUrl&))); +} + +void QtNAMThreadSafeProxy::localSetCookies(const QUrl& url, const QString& cookies) +{ + QList<QNetworkCookie> cookieList = QNetworkCookie::parseCookies(cookies.toAscii()); + QList<QNetworkCookie>::Iterator it = cookieList.begin(); + while (it != cookieList.end()) { + if (it->isHttpOnly()) + it = cookieList.erase(it); + else + ++it; + } + m_manager->cookieJar()->setCookiesFromUrl(cookieList, url); +} + +void QtNAMThreadSafeProxy::localCookiesForUrl(QFutureInterface<QList<QNetworkCookie> > fi, const QUrl& url) +{ + fi.reportResult(m_manager->cookieJar()->cookiesForUrl(url)); + fi.reportFinished(); +} + +void QtNAMThreadSafeProxy::localWillLoadFromCache(QFutureInterface<bool> fi, const QUrl& url) +{ + bool retVal = false; + if (m_manager->cache()) + retVal = m_manager->cache()->metaData(url).isValid(); + + fi.reportFinished(&retVal); +} + +QtNetworkReplyThreadSafeProxy::QtNetworkReplyThreadSafeProxy(QNetworkAccessManager *manager) + : m_manager(manager) + , m_reply(0) + , m_forwardingDefered(false) + , m_contentLengthHeader(0) + , m_error(QNetworkReply::NoError) + , m_httpStatusCode(0) +{ + moveToThread(manager->thread()); + + // This might be unnecessarily heavy to do for each request while we could have the same wrapper for the manager instead + connect(this, SIGNAL(localGetRequested(const QNetworkRequest&)), SLOT(localGet(const QNetworkRequest&))); + connect(this, SIGNAL(localPostRequested(const QNetworkRequest&, QIODevice*)), SLOT(localPost(const QNetworkRequest&, QIODevice*))); + connect(this, SIGNAL(localHeadRequested(const QNetworkRequest&)), SLOT(localHead(const QNetworkRequest&))); + connect(this, SIGNAL(localPutRequested(const QNetworkRequest&, QIODevice*)), SLOT(localPut(const QNetworkRequest&, QIODevice*))); + connect(this, SIGNAL(localDeleteResourceRequested(const QNetworkRequest&)), SLOT(localDeleteResource(const QNetworkRequest&))); + connect(this, SIGNAL(localCustomRequestRequested(const QNetworkRequest&, const QByteArray&)), SLOT(localCustomRequest(const QNetworkRequest&, const QByteArray&))); + connect(this, SIGNAL(localAbortRequested()), SLOT(localAbort())); + connect(this, SIGNAL(localSetForwardingDeferedRequested(bool)), SLOT(localSetForwardingDefered(bool))); +} + +QtNetworkReplyThreadSafeProxy::~QtNetworkReplyThreadSafeProxy() +{ + delete m_reply; +} + +void QtNetworkReplyThreadSafeProxy::localGet(const QNetworkRequest& request) +{ + localSetReply(m_manager->get(request)); +} + +void QtNetworkReplyThreadSafeProxy::localPost(const QNetworkRequest& request, QIODevice* data) +{ + localSetReply(m_manager->post(request, data)); +} + +void QtNetworkReplyThreadSafeProxy::localHead(const QNetworkRequest& request) +{ + localSetReply(m_manager->head(request)); +} + +void QtNetworkReplyThreadSafeProxy::localPut(const QNetworkRequest& request, QIODevice* data) +{ + localSetReply(m_manager->put(request, data)); +} + +void QtNetworkReplyThreadSafeProxy::localDeleteResource(const QNetworkRequest& request) +{ + localSetReply(m_manager->deleteResource(request)); +} + +void QtNetworkReplyThreadSafeProxy::localCustomRequest(const QNetworkRequest& request, const QByteArray& verb) +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + localSetReply(m_manager->sendCustomRequest(request, verb)); +#endif +} + +void QtNetworkReplyThreadSafeProxy::localAbort() +{ + if (m_reply) + m_reply->abort(); +} + +void QtNetworkReplyThreadSafeProxy::localForwardData() +{ + if (m_reply->bytesAvailable()) { + QByteArray data = m_reply->read(m_reply->bytesAvailable()); + emit dataReceived(data); + } +} + +void QtNetworkReplyThreadSafeProxy::localSetForwardingDefered(bool forwardingDefered) +{ + if (m_forwardingDefered && !forwardingDefered) + localForwardData(); + m_forwardingDefered = forwardingDefered; +} + +void QtNetworkReplyThreadSafeProxy::localMirrorMembers() +{ + ASSERT(m_reply); + QMutexLocker lock(&m_mirroredMembersMutex); + + m_contentDispositionHeader = m_reply->rawHeader("Content-Disposition"); + m_contentLengthHeader = m_reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(); + m_contentTypeHeader = m_reply->header(QNetworkRequest::ContentTypeHeader).toString(); + m_error = m_reply->error(); + m_errorString = m_reply->errorString(); + m_httpReasonPhrase = m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray(); + m_httpStatusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + m_url = m_reply->url(); + m_redirectionTarget = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + m_rawHeaderPairs = m_reply->rawHeaderPairs(); +#else + m_rawHeaderPairs.clear(); + foreach (const QByteArray& headerName, m_reply->rawHeaderList()) + m_rawHeaderPairs.append(RawHeaderPair(headerName, m_reply->rawHeader(headerName))); +#endif +} + +void QtNetworkReplyThreadSafeProxy::localSetReply(QNetworkReply *reply) +{ + ASSERT(!m_reply); + m_reply = reply; + m_reply->setParent(0); + connect(m_reply, SIGNAL(readyRead()), this, SLOT(localForwardData())); + // Make sure localMirrorMembers() is called before the outward signal + connect(m_reply, SIGNAL(finished()), this, SLOT(localMirrorMembers()), Qt::DirectConnection); + connect(m_reply, SIGNAL(finished()), this, SIGNAL(finished())); + // Make sure localMirrorMembers() is called before the outward signal + connect(m_reply, SIGNAL(metaDataChanged()), this, SLOT(localMirrorMembers()), Qt::DirectConnection); + connect(m_reply, SIGNAL(metaDataChanged()), this, SIGNAL(metaDataChanged())); + connect(m_reply, SIGNAL(uploadProgress(qint64, qint64)), this, SIGNAL(uploadProgress(qint64, qint64))); +} + + +} diff --git a/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h b/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h new file mode 100644 index 0000000..3e0c189 --- /dev/null +++ b/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h @@ -0,0 +1,186 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef QtNAMThreadSafeProxy_h +#define QtNAMThreadSafeProxy_h + +#include <QFuture> +#include <QMutex> +#include <QNetworkCookie> +#include <QNetworkReply> +#include <QObject> + +class QNetworkAccessManager; +class QNetworkRequest; +class QUrl; + +namespace WebCore { + +class QtNAMThreadSafeProxy : public QObject { + Q_OBJECT +public: + QtNAMThreadSafeProxy(QNetworkAccessManager *manager); + + void setCookies(const QUrl& url, const QString& cookies) + { + emit localSetCookiesRequested(url, cookies); + } + + QFuture<QList<QNetworkCookie> > cookiesForUrl(const QUrl& url) + { + QFutureInterface<QList<QNetworkCookie> > fi; + fi.reportStarted(); + emit localCookiesForUrlRequested(fi, url); + return fi.future(); + } + + QFuture<bool> willLoadFromCache(const QUrl& url) + { + QFutureInterface<bool> fi; + fi.reportStarted(); + emit localWillLoadFromCacheRequested(fi, url); + return fi.future(); + } + +signals: + void localSetCookiesRequested(const QUrl& url, const QString& cookies); + void localCookiesForUrlRequested(QFutureInterface<QList<QNetworkCookie> > fi, const QUrl& url); + void localWillLoadFromCacheRequested(QFutureInterface<bool> fi, const QUrl& url); + +private slots: + void localSetCookies(const QUrl& url, const QString& cookies); + void localCookiesForUrl(QFutureInterface<QList<QNetworkCookie> > fi, const QUrl& url); + void localWillLoadFromCache(QFutureInterface<bool> fi, const QUrl& url); + +private: + QNetworkAccessManager* m_manager; +}; + + +class QtNetworkReplyThreadSafeProxy : public QObject { + Q_OBJECT +public: + typedef QPair<QByteArray, QByteArray> RawHeaderPair; + QtNetworkReplyThreadSafeProxy(QNetworkAccessManager *manager); + ~QtNetworkReplyThreadSafeProxy(); + void abort() + { + emit localAbortRequested(); + } + void setForwardingDefered(bool forwardingDefered) + { + emit localSetForwardingDeferedRequested(forwardingDefered); + } + + QByteArray contentDispositionHeader() { QMutexLocker lock(&m_mirroredMembersMutex); return m_contentDispositionHeader; } + qlonglong contentLengthHeader() { QMutexLocker lock(&m_mirroredMembersMutex); return m_contentLengthHeader; } + QString contentTypeHeader() { QMutexLocker lock(&m_mirroredMembersMutex); return m_contentTypeHeader; } + QNetworkReply::NetworkError error() { QMutexLocker lock(&m_mirroredMembersMutex); return m_error; } + QString errorString() { QMutexLocker lock(&m_mirroredMembersMutex); return m_errorString; } + QByteArray httpReasonPhrase() { QMutexLocker lock(&m_mirroredMembersMutex); return m_httpReasonPhrase; } + int httpStatusCode() { QMutexLocker lock(&m_mirroredMembersMutex); return m_httpStatusCode; } + QUrl url() { QMutexLocker lock(&m_mirroredMembersMutex); return m_url; } + QUrl redirectionTarget() { QMutexLocker lock(&m_mirroredMembersMutex); return m_redirectionTarget; } + QList<RawHeaderPair> rawHeaderPairs() { QMutexLocker lock(&m_mirroredMembersMutex); return m_rawHeaderPairs; } + + QNetworkReply* reply() + { + // Careful, acccessing the reply accross threads might be hazardous to your health + return m_reply; + } +public: + void get(const QNetworkRequest &request) + { + emit localGetRequested(request); + } + void post(const QNetworkRequest &request, QIODevice* data) + { + emit localPostRequested(request, data); + } + void head(const QNetworkRequest &request) + { + emit localHeadRequested(request); + } + void put(const QNetworkRequest &request, QIODevice* data) + { + emit localPutRequested(request, data); + } + void deleteResource(const QNetworkRequest &request) + { + emit localDeleteResourceRequested(request); + } +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + void sendCustomRequest(const QNetworkRequest &request, const QByteArray& verb) + { + emit localCustomRequestRequested(request, verb); + } +#endif + +signals: + void localGetRequested(const QNetworkRequest& request); + void localPostRequested(const QNetworkRequest& request, QIODevice* data); + void localHeadRequested(const QNetworkRequest& request); + void localPutRequested(const QNetworkRequest& request, QIODevice* data); + void localDeleteResourceRequested(const QNetworkRequest& request); + void localCustomRequestRequested(const QNetworkRequest& request, const QByteArray& verb); + void localAbortRequested(); + void localSetForwardingDeferedRequested(bool forwardingDefered); + + void finished(); + void readyRead(); + void metaDataChanged(); + void uploadProgress(qint64 bytesSent, qint64 bytesTotal); + void dataReceived(const QByteArray &data); + +private slots: + void localGet(const QNetworkRequest& request); + void localPost(const QNetworkRequest& request, QIODevice* data); + void localHead(const QNetworkRequest& request); + void localPut(const QNetworkRequest& request, QIODevice* data); + void localDeleteResource(const QNetworkRequest& request); + void localCustomRequest(const QNetworkRequest& request, const QByteArray& verb); + void localAbort(); + void localForwardData(); + void localSetForwardingDefered(bool forwardingDefered); + void localMirrorMembers(); + +private: + void localSetReply(QNetworkReply *reply); + + QNetworkAccessManager *m_manager; + QNetworkReply *m_reply; + bool m_forwardingDefered; + + // Mirrored members + QMutex m_mirroredMembersMutex; + QByteArray m_contentDispositionHeader; + qlonglong m_contentLengthHeader; + QString m_contentTypeHeader; + QNetworkReply::NetworkError m_error; + QString m_errorString; + QByteArray m_httpReasonPhrase; + int m_httpStatusCode; + QUrl m_url; + QUrl m_redirectionTarget; + QList<RawHeaderPair> m_rawHeaderPairs; +}; + +} + + +#endif // QtNAMThreadSafeProxy_h diff --git a/WebCore/platform/network/qt/ResourceHandleQt.cpp b/WebCore/platform/network/qt/ResourceHandleQt.cpp index a5ac4c3..f40b828 100644 --- a/WebCore/platform/network/qt/ResourceHandleQt.cpp +++ b/WebCore/platform/network/qt/ResourceHandleQt.cpp @@ -35,6 +35,7 @@ #include "Frame.h" #include "FrameNetworkingContext.h" #include "FrameLoaderClientQt.h" +#include "QtNAMThreadSafeProxy.h" #include "NotImplemented.h" #include "Page.h" #include "QNetworkReplyHandler.h" @@ -156,20 +157,13 @@ bool ResourceHandle::willLoadFromCache(ResourceRequest& request, Frame* frame) if (!frame) return false; - QNetworkAccessManager* manager = 0; - QAbstractNetworkCache* cache = 0; if (frame->loader()->networkingContext()) { - manager = frame->loader()->networkingContext()->networkAccessManager(); - cache = manager->cache(); - } - - if (!cache) - return false; - - QNetworkCacheMetaData data = cache->metaData(request.url()); - if (data.isValid()) { - request.setCachePolicy(ReturnCacheDataDontLoad); - return true; + QNetworkAccessManager* manager = frame->loader()->networkingContext()->networkAccessManager(); + QtNAMThreadSafeProxy managerProxy(manager); + if (managerProxy.willLoadFromCache(request.url())) { + request.setCachePolicy(ReturnCacheDataDontLoad); + return true; + } } return false; diff --git a/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/WebCore/platform/network/soup/ResourceHandleSoup.cpp index c1933e3..a2440d3 100644 --- a/WebCore/platform/network/soup/ResourceHandleSoup.cpp +++ b/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -492,7 +492,7 @@ static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer use if (isTransportError || (error->domain == G_IO_ERROR)) { SoupURI* uri = webkit_soup_request_get_uri(d->m_soupRequest.get()); GOwnPtr<char> uriStr(soup_uri_to_string(uri, false)); - gint errorCode = isTransportError ? soupMsg->status_code : error->code; + gint errorCode = isTransportError ? static_cast<gint>(soupMsg->status_code) : error->code; const gchar* errorMsg = isTransportError ? soupMsg->reason_phrase : error->message; const gchar* quarkStr = isTransportError ? g_quark_to_string(SOUP_HTTP_ERROR) : g_quark_to_string(G_IO_ERROR); ResourceError resourceError(quarkStr, errorCode, uriStr.get(), String::fromUTF8(errorMsg)); @@ -508,8 +508,11 @@ static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer use fillResponseFromMessage(soupMsg, &d->m_response); client->didReceiveResponse(handle.get(), d->m_response); - // WebCore might have cancelled the job in the while - if (!d->m_cancelled && soupMsg->response_body->data) + // WebCore might have cancelled the job in the while. We + // must check for response_body->length and not + // response_body->data as libsoup always creates the + // SoupBuffer for the body even if the length is 0 + if (!d->m_cancelled && soupMsg->response_body->length) client->didReceiveData(handle.get(), soupMsg->response_body->data, soupMsg->response_body->length, true); } @@ -550,6 +553,9 @@ static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer use } } + if (d->m_defersLoading) + soup_session_pause_message(handle->defaultSession(), d->m_soupMessage.get()); + g_input_stream_read_async(d->m_inputStream.get(), d->m_buffer, READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, d->m_cancellable.get(), readCallback, 0); } @@ -660,8 +666,11 @@ static bool startHttp(ResourceHandle* handle) if (!soup_message_headers_get_one(soupMessage->request_headers, "Accept")) soup_message_headers_append(soupMessage->request_headers, "Accept", "*/*"); - d->m_cancellable = adoptPlatformRef(g_cancellable_new()); - webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + // Send the request only if it's not been explicitely deferred. + if (!d->m_defersLoading) { + d->m_cancellable = adoptPlatformRef(g_cancellable_new()); + webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + } return true; } @@ -734,9 +743,28 @@ bool ResourceHandle::supportsBufferedData() return false; } -void ResourceHandle::platformSetDefersLoading(bool) +void ResourceHandle::platformSetDefersLoading(bool defersLoading) { - notImplemented(); + // Initial implementation of this method was required for bug #44157. + + if (d->m_cancelled) + return; + + if (!defersLoading && !d->m_cancellable && d->m_soupRequest.get()) { + d->m_cancellable = adoptPlatformRef(g_cancellable_new()); + webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + return; + } + + // Only supported for http(s) transfers. Something similar would + // probably be needed for data transfers done with GIO. + if (!d->m_soupMessage) + return; + + if (defersLoading) + soup_session_pause_message(defaultSession(), d->m_soupMessage.get()); + else + soup_session_unpause_message(defaultSession(), d->m_soupMessage.get()); } bool ResourceHandle::loadsBlocked() @@ -758,7 +786,7 @@ void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const // 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, true, false)); + RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, &syncLoader, false /*defersLoading*/, false /*shouldContentSniff*/)); handle->start(context); syncLoader.run(); diff --git a/WebCore/platform/network/soup/cache/soup-http-input-stream.c b/WebCore/platform/network/soup/cache/soup-http-input-stream.c index dc95d6e..195c458 100644 --- a/WebCore/platform/network/soup/cache/soup-http-input-stream.c +++ b/WebCore/platform/network/soup/cache/soup-http-input-stream.c @@ -598,6 +598,7 @@ send_async_finished (GInputStream *stream) g_error_free (error); } g_simple_async_result_complete (result); + g_object_unref (result); } static void diff --git a/WebCore/platform/network/soup/cache/soup-request-http.c b/WebCore/platform/network/soup/cache/soup-request-http.c index f3fe2ec..777fd72 100644 --- a/WebCore/platform/network/soup/cache/soup-request-http.c +++ b/WebCore/platform/network/soup/cache/soup-request-http.c @@ -188,6 +188,7 @@ typedef struct { WebKitSoupRequestHTTP *http; GAsyncReadyCallback callback; gpointer user_data; + WebKitSoupHTTPInputStream *httpstream; } SendAsyncHelper; static void webkit_soup_request_http_send_async (WebKitSoupRequest *request, @@ -199,45 +200,34 @@ static gboolean send_async_cb (gpointer data) { GSimpleAsyncResult *simple; - WebKitSoupHTTPInputStream *httpstream; - SoupSession *session; - WebKitSoupCache *cache; SendAsyncHelper *helper = (SendAsyncHelper *)data; + const gchar *content_type; - session = webkit_soup_request_get_session (WEBKIT_SOUP_REQUEST (helper->http)); - cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE); - - httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, SOUP_MESSAGE (helper->http->priv->msg)); - - if (httpstream) { - const gchar *content_type; - - simple = g_simple_async_result_new (G_OBJECT (helper->http), - helper->callback, helper->user_data, - webkit_soup_request_http_send_async); - g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref); + 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); + /* Update message status */ + soup_message_set_status (helper->http->priv->msg, SOUP_STATUS_OK); - /* Issue signals */ - soup_message_got_headers (helper->http->priv->msg); + /* 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); + /* 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); + g_simple_async_result_complete (simple); - soup_message_finished (helper->http->priv->msg); + soup_message_finished (helper->http->priv->msg); - g_object_unref (simple); - } + g_object_unref (simple); g_object_unref (helper->http); g_slice_free (SendAsyncHelper, helper); @@ -265,17 +255,28 @@ webkit_soup_request_http_send_async (WebKitSoupRequest *request, response = webkit_soup_cache_has_response (cache, http->priv->msg); if (response == WEBKIT_SOUP_CACHE_RESPONSE_FRESH) { - /* Do return the stream asynchronously as in - the other cases. It's not enough to use - g_simple_async_result_complete_in_idle as - the signals must be also emitted - asynchronously */ - SendAsyncHelper *helper = g_slice_new (SendAsyncHelper); - helper->http = g_object_ref (http); - helper->callback = callback; - helper->user_data = user_data; - g_timeout_add (0, send_async_cb, helper); - return; + 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; diff --git a/WebCore/platform/network/soup/cache/webkit/soup-cache.c b/WebCore/platform/network/soup/cache/webkit/soup-cache.c index 4835750..b96428d 100644 --- a/WebCore/platform/network/soup/cache/webkit/soup-cache.c +++ b/WebCore/platform/network/soup/cache/webkit/soup-cache.c @@ -147,6 +147,8 @@ get_cacheability (WebKitSoupCache *cache, SoupMessage *msg) soup_header_free_param_list (hash); return WEBKIT_SOUP_CACHE_UNCACHEABLE; } + + soup_header_free_param_list (hash); } switch (msg->status_code) { @@ -284,14 +286,12 @@ webkit_soup_cache_entry_set_freshness (WebKitSoupCacheEntry *entry, SoupMessage { const char *cache_control; const char *expires, *date, *last_modified; - GHashTable *hash; - - hash = NULL; cache_control = soup_message_headers_get (entry->headers, "Cache-Control"); if (cache_control) { const char *max_age, *s_maxage; gint64 freshness_lifetime = 0; + GHashTable *hash; WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache); hash = soup_header_parse_param_list (cache_control); @@ -323,10 +323,9 @@ webkit_soup_cache_entry_set_freshness (WebKitSoupCacheEntry *entry, SoupMessage soup_header_free_param_list (hash); return; } - } - if (hash != NULL) soup_header_free_param_list (hash); + } /* If the 'Expires' response header is present, use its value * minus the value of the 'Date' response header @@ -998,6 +997,7 @@ webkit_soup_cache_send_response (WebKitSoupCache *cache, SoupMessage *msg) 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 @@ -1084,11 +1084,12 @@ webkit_soup_cache_init (WebKitSoupCache *cache) } static void -remove_cache_item (gpointer key, - gpointer value, - WebKitSoupCache *cache) +remove_cache_item (gpointer data, + gpointer user_data) { - WebKitSoupCacheEntry *entry = g_hash_table_lookup (cache->priv->cache, (const gchar *)key); + WebKitSoupCache *cache = (WebKitSoupCache *) user_data; + WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data; + if (webkit_soup_cache_entry_remove (cache, entry)) webkit_soup_cache_entry_free (entry, FALSE); } @@ -1097,10 +1098,15 @@ static void webkit_soup_cache_finalize (GObject *object) { WebKitSoupCachePrivate *priv; + GList *entries; priv = WEBKIT_SOUP_CACHE (object)->priv; - g_hash_table_foreach (priv->cache, (GHFunc)remove_cache_item, object); + // 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); @@ -1257,7 +1263,6 @@ webkit_soup_cache_has_response (WebKitSoupCache *cache, SoupMessage *msg) char *key; WebKitSoupCacheEntry *entry; const char *cache_control; - GHashTable *hash; gpointer value; gboolean must_revalidate; int max_age, max_stale, min_fresh; @@ -1265,6 +1270,7 @@ webkit_soup_cache_has_response (WebKitSoupCache *cache, SoupMessage *msg) 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 @@ -1321,10 +1327,10 @@ webkit_soup_cache_has_response (WebKitSoupCache *cache, SoupMessage *msg) cache_control = soup_message_headers_get (msg->request_headers, "Cache-Control"); if (cache_control) { - hash = soup_header_parse_param_list (cache_control); + GHashTable *hash = soup_header_parse_param_list (cache_control); if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) { - g_hash_table_destroy (hash); + soup_header_free_param_list (hash); return WEBKIT_SOUP_CACHE_RESPONSE_STALE; } @@ -1348,7 +1354,7 @@ webkit_soup_cache_has_response (WebKitSoupCache *cache, SoupMessage *msg) if (value) min_fresh = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32); - g_hash_table_destroy (hash); + soup_header_free_param_list (hash); if (max_age != -1) { guint current_age = webkit_soup_cache_entry_get_current_age (entry); @@ -1449,11 +1455,12 @@ webkit_soup_cache_flush (WebKitSoupCache *cache) } static void -clear_cache_item (gpointer key, - gpointer value, - WebKitSoupCache *cache) +clear_cache_item (gpointer data, + gpointer user_data) { - WebKitSoupCacheEntry *entry = g_hash_table_lookup (cache->priv->cache, (const gchar *)key); + WebKitSoupCache *cache = (WebKitSoupCache *) user_data; + WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data; + if (webkit_soup_cache_entry_remove (cache, entry)) webkit_soup_cache_entry_free (entry, TRUE); } @@ -1469,13 +1476,17 @@ 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); - g_hash_table_foreach (hash, (GHFunc)clear_cache_item, cache); + // 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 * @@ -1604,12 +1615,13 @@ webkit_soup_cache_load (WebKitSoupCache *cache) 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, NULL, NULL); + 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); |