Merge branch 'master' of git://scm.dev.nokia.troll.no/qt/qtbase-staging
authorQt Continuous Integration System <qt-info@nokia.com>
Fri, 6 May 2011 14:05:46 +0000 (00:05 +1000)
committerQt Continuous Integration System <qt-info@nokia.com>
Fri, 6 May 2011 14:05:46 +0000 (00:05 +1000)
* 'master' of git://scm.dev.nokia.troll.no/qt/qtbase-staging:
  Fix QRawFont::setPixelSize() on Mac

15 files changed:
src/network/access/access.pri
src/network/access/qnetworkaccessbackend.cpp
src/network/access/qnetworkaccesscachebackend.cpp
src/network/access/qnetworkaccessmanager.cpp
src/network/access/qnetworkaccessmanager.h
src/network/access/qnetworkaccessmanager_p.h
src/network/access/qnetworkcookie.cpp
src/network/access/qnetworkreplyhttpimpl.cpp [new file with mode: 0644]
src/network/access/qnetworkreplyhttpimpl_p.h [new file with mode: 0644]
src/network/access/qnetworkreplyimpl.cpp
src/network/access/qnetworkrequest.cpp
src/network/ssl/qsslconfiguration.cpp
tests/auto/qabstractnetworkcache/tst_qabstractnetworkcache.cpp
tests/auto/qnetworkcookie/tst_qnetworkcookie.cpp
tests/auto/qnetworkreply/tst_qnetworkreply.cpp

index 5ead3ad..0f901b8 100644 (file)
@@ -14,7 +14,6 @@ HEADERS += \
     access/qnetworkaccesscache_p.h \
     access/qnetworkaccessbackend_p.h \
     access/qnetworkaccessdebugpipebackend_p.h \
-    access/qnetworkaccesshttpbackend_p.h \
     access/qnetworkaccessfilebackend_p.h \
     access/qnetworkaccesscachebackend_p.h \
     access/qnetworkaccessftpbackend_p.h \
@@ -29,6 +28,7 @@ HEADERS += \
     access/qnetworkreply_p.h \
     access/qnetworkreplyimpl_p.h \
     access/qnetworkreplydataimpl_p.h \
+    access/qnetworkreplyhttpimpl_p.h \
     access/qnetworkreplyfileimpl_p.h \
     access/qabstractnetworkcache_p.h \
     access/qabstractnetworkcache.h \
@@ -54,13 +54,13 @@ SOURCES += \
     access/qnetworkaccessfilebackend.cpp \
     access/qnetworkaccesscachebackend.cpp \
     access/qnetworkaccessftpbackend.cpp \
-    access/qnetworkaccesshttpbackend.cpp \
     access/qnetworkcookie.cpp \
     access/qnetworkcookiejar.cpp \
     access/qnetworkrequest.cpp \
     access/qnetworkreply.cpp \
     access/qnetworkreplyimpl.cpp \
     access/qnetworkreplydataimpl.cpp \
+    access/qnetworkreplyhttpimpl.cpp \
     access/qnetworkreplyfileimpl.cpp \
     access/qabstractnetworkcache.cpp \
     access/qnetworkdiskcache.cpp \
index 6220abe..8a53d2d 100644 (file)
@@ -315,13 +315,13 @@ void QNetworkAccessBackend::error(QNetworkReply::NetworkError code, const QStrin
 void QNetworkAccessBackend::proxyAuthenticationRequired(const QNetworkProxy &proxy,
                                                         QAuthenticator *authenticator)
 {
-    manager->proxyAuthenticationRequired(this, proxy, authenticator);
+    manager->proxyAuthenticationRequired(proxy, synchronous, authenticator, &reply->lastProxyAuthentication);
 }
 #endif
 
 void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator)
 {
-    manager->authenticationRequired(this, authenticator);
+    manager->authenticationRequired(authenticator, reply->q_func(), synchronous, reply->url, &reply->urlForLastAuthentication);
 }
 
 void QNetworkAccessBackend::metaDataChanged()
index 13f4cd9..c585848 100644 (file)
@@ -66,6 +66,7 @@ void QNetworkAccessCacheBackend::open()
         QString msg = QCoreApplication::translate("QNetworkAccessCacheBackend", "Error opening %1")
                                                 .arg(this->url().toString());
         error(QNetworkReply::ContentNotFoundError, msg);
+    } else {
         setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
     }
     finished();
@@ -85,14 +86,18 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
     QNetworkCacheMetaData::AttributesMap attributes = item.attributes();
     setAttribute(QNetworkRequest::HttpStatusCodeAttribute, attributes.value(QNetworkRequest::HttpStatusCodeAttribute));
     setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
-    setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
 
     // set the raw headers
     QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders();
     QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(),
                                                        end = rawHeaders.constEnd();
-    for ( ; it != end; ++it)
+    for ( ; it != end; ++it) {
+        if (it->first.toLower() == "cache-control" &&
+            it->second.toLower().contains("must-revalidate")) {
+            return false;
+        }
         setRawHeader(it->first, it->second);
+    }
 
     // handle a possible redirect
     QVariant redirectionTarget = attributes.value(QNetworkRequest::RedirectionTargetAttribute);
index 5a7521e..090a25c 100644 (file)
 #include "QtNetwork/qhttpmultipart.h"
 #include "qhttpmultipart_p.h"
 
+#include "qnetworkreplyhttpimpl_p.h"
+
 #include "qthread.h"
 
 QT_BEGIN_NAMESPACE
 
-#ifndef QT_NO_HTTP
-Q_GLOBAL_STATIC(QNetworkAccessHttpBackendFactory, httpBackend)
-#endif // QT_NO_HTTP
 Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
 #ifndef QT_NO_FTP
 Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend)
@@ -85,10 +84,6 @@ Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend)
 
 static void ensureInitialized()
 {
-#ifndef QT_NO_HTTP
-    (void) httpBackend();
-#endif // QT_NO_HTTP
-
 #ifndef QT_NO_FTP
     (void) ftpBackend();
 #endif
@@ -356,6 +351,17 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
     ensureInitialized();
 
     qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
+#ifndef QT_NO_NETWORKPROXY
+    qRegisterMetaType<QNetworkProxy>("QNetworkProxy");
+#endif
+#ifndef QT_NO_OPENSSL
+    qRegisterMetaType<QList<QSslError> >("QList<QSslError>");
+    qRegisterMetaType<QSslConfiguration>("QSslConfiguration");
+#endif
+    qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >("QList<QPair<QByteArray,QByteArray> >");
+    qRegisterMetaType<QHttpNetworkRequest>("QHttpNetworkRequest");
+    qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
+    qRegisterMetaType<QSharedPointer<char> >("QSharedPointer<char>");
 }
 
 /*!
@@ -967,6 +973,18 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
         }
     }
 
+#ifndef QT_NO_HTTP
+    // Since Qt 5 we use the new QNetworkReplyHttpImpl
+    if (scheme == QLatin1String("http") || scheme == QLatin1String("https") ) {
+        QNetworkReplyHttpImpl *reply = new QNetworkReplyHttpImpl(this, request, op, outgoingData);
+#ifndef QT_NO_BEARERMANAGEMENT
+        connect(this, SIGNAL(networkSessionConnected()),
+                reply, SLOT(_q_networkSessionConnected()));
+#endif
+        return reply;
+    }
+#endif // QT_NO_HTTP
+
     // first step: create the reply
     QUrl url = request.url();
     QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
@@ -1055,42 +1073,43 @@ void QNetworkAccessManagerPrivate::createCookieJar() const
     }
 }
 
-void QNetworkAccessManagerPrivate::authenticationRequired(QNetworkAccessBackend *backend,
-                                                          QAuthenticator *authenticator)
+void QNetworkAccessManagerPrivate::authenticationRequired(QAuthenticator *authenticator,
+                                                          QNetworkReply *reply,
+                                                          bool synchronous,
+                                                          QUrl &url,
+                                                          QUrl *urlForLastAuthentication)
 {
     Q_Q(QNetworkAccessManager);
 
-    // FIXME: Add support for domains (i.e., the leading path)
-    QUrl url = backend->reply->url;
-
     // don't try the cache for the same URL twice in a row
     // being called twice for the same URL means the authentication failed
     // also called when last URL is empty, e.g. on first call
-    if (backend->reply->urlForLastAuthentication.isEmpty()
-            || url != backend->reply->urlForLastAuthentication) {
+    if (urlForLastAuthentication->isEmpty()
+            || url != *urlForLastAuthentication) {
         QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedCredentials(url, authenticator);
         if (!cred.isNull()) {
             authenticator->setUser(cred.user);
             authenticator->setPassword(cred.password);
-            backend->reply->urlForLastAuthentication = url;
+            *urlForLastAuthentication = url;
             return;
         }
     }
 
     // if we emit a signal here in synchronous mode, the user might spin
     // an event loop, which might recurse and lead to problems
-    if (backend->isSynchronous())
+    if (synchronous)
         return;
 
-    backend->reply->urlForLastAuthentication = url;
-    emit q->authenticationRequired(backend->reply->q_func(), authenticator);
+    *urlForLastAuthentication = url;
+    emit q->authenticationRequired(reply, authenticator);
     authenticationManager->cacheCredentials(url, authenticator);
 }
 
 #ifndef QT_NO_NETWORKPROXY
-void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBackend *backend,
-                                                               const QNetworkProxy &proxy,
-                                                               QAuthenticator *authenticator)
+void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(const QNetworkProxy &proxy,
+                                                               bool synchronous,
+                                                               QAuthenticator *authenticator,
+                                                               QNetworkProxy *lastProxyAuthentication)
 {
     Q_Q(QNetworkAccessManager);
     // ### FIXME Tracking of successful authentications
@@ -1100,7 +1119,7 @@ void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBac
     //      proxyAuthenticationRequired gets emitted again
     // possible solution: some tracking inside the authenticator
     //      or a new function proxyAuthenticationSucceeded(true|false)
-    if (proxy != backend->reply->lastProxyAuthentication) {
+    if (proxy != *lastProxyAuthentication) {
         QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy);
         if (!cred.isNull()) {
             authenticator->setUser(cred.user);
@@ -1111,10 +1130,10 @@ void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBac
 
     // if we emit a signal here in synchronous mode, the user might spin
     // an event loop, which might recurse and lead to problems
-    if (backend->isSynchronous())
+    if (synchronous)
         return;
 
-    backend->reply->lastProxyAuthentication = proxy;
+    *lastProxyAuthentication = proxy;
     emit q->proxyAuthenticationRequired(proxy, authenticator);
     authenticationManager->cacheProxyCredentials(proxy, authenticator);
 }
index 47760b2..b22be91 100644 (file)
@@ -160,6 +160,7 @@ protected:
 private:
     friend class QNetworkReplyImplPrivate;
     friend class QNetworkAccessHttpBackend;
+    friend class QNetworkReplyHttpImpl;
 
     Q_DECLARE_PRIVATE(QNetworkAccessManager)
     Q_PRIVATE_SLOT(d_func(), void _q_replyFinished())
index f64cc4d..f5e13e3 100644 (file)
@@ -94,14 +94,20 @@ public:
     QNetworkReply *postProcess(QNetworkReply *reply);
     void createCookieJar() const;
 
-    void authenticationRequired(QNetworkAccessBackend *backend, QAuthenticator *authenticator);
+    void authenticationRequired(QAuthenticator *authenticator,
+                                QNetworkReply *reply,
+                                bool synchronous,
+                                QUrl &url,
+                                QUrl *urlForLastAuthentication);
     void cacheCredentials(const QUrl &url, const QAuthenticator *auth);
     QNetworkAuthenticationCredential *fetchCachedCredentials(const QUrl &url,
                                                              const QAuthenticator *auth = 0);
 
 #ifndef QT_NO_NETWORKPROXY
-    void proxyAuthenticationRequired(QNetworkAccessBackend *backend, const QNetworkProxy &proxy,
-                                     QAuthenticator *authenticator);
+    void proxyAuthenticationRequired(const QNetworkProxy &proxy,
+                                bool synchronous,
+                                QAuthenticator *authenticator,
+                                QNetworkProxy *lastProxyAuthentication);
     void cacheProxyCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth);
     QNetworkAuthenticationCredential *fetchCachedProxyCredentials(const QNetworkProxy &proxy,
                                                              const QAuthenticator *auth = 0);
index 52eb345..eec8507 100644 (file)
@@ -395,8 +395,8 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
         // qdtext         = <any TEXT except <">>
         // quoted-pair    = "\" CHAR
 
-        // If its NAME=VALUE, retain the value as is
-        // refer to ttp://bugreports.qt.nokia.com/browse/QTBUG-17746
+        // If it is NAME=VALUE, retain the value as is
+        // refer to http://bugreports.qt.nokia.com/browse/QTBUG-17746
         if (isNameValue)
             second += '"';
         ++i;
@@ -432,7 +432,9 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
         position = i;
         for ( ; i < length; ++i) {
             register char c = text.at(i);
-            if (c == ',' || c == ';' || isLWS(c))
+            // for name value pairs, we want to parse until reaching the next ';'
+            // and not break when reaching a space char
+            if (c == ',' || c == ';' || ((isNameValue && (c == '\n' || c == '\r')) || (!isNameValue && isLWS(c))))
                 break;
         }
 
@@ -487,7 +489,6 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const
     result += '=';
     if ((d->value.contains(';') ||
         d->value.contains(',') ||
-        d->value.contains(' ') ||
         d->value.contains('"')) &&
         (!d->value.startsWith('"') &&
         !d->value.endsWith('"'))) {
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
new file mode 100644 (file)
index 0000000..cd6d8fb
--- /dev/null
@@ -0,0 +1,1983 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QNETWORKACCESSHTTPBACKEND_DEBUG
+
+#include "qnetworkreplyhttpimpl_p.h"
+#include "qnetworkaccessmanager_p.h"
+#include "qnetworkaccesscache_p.h"
+#include "qabstractnetworkcache.h"
+#include "qnetworkrequest.h"
+#include "qnetworkreply.h"
+#include "qnetworkrequest_p.h"
+#include "qnetworkcookie_p.h"
+#include "QtCore/qdatetime.h"
+#include "QtCore/qelapsedtimer.h"
+#include "QtNetwork/qsslconfiguration.h"
+#include "qhttpthreaddelegate_p.h"
+#include "qthread.h"
+#include "QtCore/qcoreapplication.h"
+
+#include "qnetworkcookiejar.h"
+
+#ifndef QT_NO_HTTP
+
+#include <string.h>             // for strchr
+
+Q_DECLARE_METATYPE(QSharedPointer<char>)
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkProxy;
+
+static inline bool isSeparator(register char c)
+{
+    static const char separators[] = "()<>@,;:\\\"/[]?={}";
+    return isLWS(c) || strchr(separators, c) != 0;
+}
+
+// ### merge with nextField in cookiejar.cpp
+static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &header)
+{
+    // The HTTP header is of the form:
+    // header          = #1(directives)
+    // directives      = token | value-directive
+    // value-directive = token "=" (token | quoted-string)
+    QHash<QByteArray, QByteArray> result;
+
+    int pos = 0;
+    while (true) {
+        // skip spaces
+        pos = nextNonWhitespace(header, pos);
+        if (pos == header.length())
+            return result;      // end of parsing
+
+        // pos points to a non-whitespace
+        int comma = header.indexOf(',', pos);
+        int equal = header.indexOf('=', pos);
+        if (comma == pos || equal == pos)
+            // huh? Broken header.
+            return result;
+
+        // The key name is delimited by either a comma, an equal sign or the end
+        // of the header, whichever comes first
+        int end = comma;
+        if (end == -1)
+            end = header.length();
+        if (equal != -1 && end > equal)
+            end = equal;        // equal sign comes before comma/end
+        QByteArray key = QByteArray(header.constData() + pos, end - pos).trimmed().toLower();
+        pos = end + 1;
+
+        if (uint(equal) < uint(comma)) {
+            // case: token "=" (token | quoted-string)
+            // skip spaces
+            pos = nextNonWhitespace(header, pos);
+            if (pos == header.length())
+                // huh? Broken header
+                return result;
+
+            QByteArray value;
+            value.reserve(header.length() - pos);
+            if (header.at(pos) == '"') {
+                // case: quoted-string
+                // quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
+                // qdtext         = <any TEXT except <">>
+                // quoted-pair    = "\" CHAR
+                ++pos;
+                while (pos < header.length()) {
+                    register char c = header.at(pos);
+                    if (c == '"') {
+                        // end of quoted text
+                        break;
+                    } else if (c == '\\') {
+                        ++pos;
+                        if (pos >= header.length())
+                            // broken header
+                            return result;
+                        c = header.at(pos);
+                    }
+
+                    value += c;
+                    ++pos;
+                }
+            } else {
+                // case: token
+                while (pos < header.length()) {
+                    register char c = header.at(pos);
+                    if (isSeparator(c))
+                        break;
+                    value += c;
+                    ++pos;
+                }
+            }
+
+            result.insert(key, value);
+
+            // find the comma now:
+            comma = header.indexOf(',', pos);
+            if (comma == -1)
+                return result;  // end of parsing
+            pos = comma + 1;
+        } else {
+            // case: token
+            // key is already set
+            result.insert(key, QByteArray());
+        }
+    }
+}
+
+QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manager,
+                                             const QNetworkRequest& request,
+                                             QNetworkAccessManager::Operation& operation,
+                                             QIODevice* outgoingData)
+    : QNetworkReply(*new QNetworkReplyHttpImplPrivate, manager)
+{
+    Q_D(QNetworkReplyHttpImpl);
+    d->manager = manager;
+    d->managerPrivate = manager->d_func();
+    d->request = request;
+    d->operation = operation;
+    d->outgoingData = outgoingData;
+    d->url = request.url();
+#ifndef QT_NO_OPENSSL
+    d->sslConfiguration = request.sslConfiguration();
+#endif
+
+    // FIXME Later maybe set to Unbuffered, especially if it is zerocopy or from cache?
+    QIODevice::open(QIODevice::ReadOnly);
+
+
+    // Internal code that does a HTTP reply for the synchronous Ajax
+    // in QtWebKit.
+    QVariant synchronousHttpAttribute = request.attribute(
+            static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
+    if (synchronousHttpAttribute.isValid()) {
+        d->synchronous = synchronousHttpAttribute.toBool();
+        if (d->synchronous && outgoingData) {
+            // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
+            // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
+            d->outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
+            qint64 previousDataSize = 0;
+            do {
+                previousDataSize = d->outgoingDataBuffer->size();
+                d->outgoingDataBuffer->append(d->outgoingData->readAll());
+            } while (d->outgoingDataBuffer->size() != previousDataSize);
+            d->_q_startOperation();
+            return;
+        }
+    }
+
+
+    if (outgoingData) {
+        // there is data to be uploaded, e.g. HTTP POST.
+
+        if (!d->outgoingData->isSequential()) {
+            // fixed size non-sequential (random-access)
+            // just start the operation
+            QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection);
+            // FIXME make direct call?
+        } else {
+            bool bufferingDisallowed =
+                    request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
+                                  false).toBool();
+
+            if (bufferingDisallowed) {
+                // if a valid content-length header for the request was supplied, we can disable buffering
+                // if not, we will buffer anyway
+                if (request.header(QNetworkRequest::ContentLengthHeader).isValid()) {
+                    QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection);
+                    // FIXME make direct call?
+                } else {
+                    d->state = d->Buffering;
+                    QMetaObject::invokeMethod(this, "_q_bufferOutgoingData", Qt::QueuedConnection);
+                }
+            } else {
+                // _q_startOperation will be called when the buffering has finished.
+                d->state = d->Buffering;
+                QMetaObject::invokeMethod(this, "_q_bufferOutgoingData", Qt::QueuedConnection);
+            }
+        }
+    } else {
+        // No outgoing data (POST, ..)
+        d->_q_startOperation();
+    }
+}
+
+QNetworkReplyHttpImpl::~QNetworkReplyHttpImpl()
+{
+    // Most work is done in private destructor
+}
+
+void QNetworkReplyHttpImpl::close()
+{
+    Q_D(QNetworkReplyHttpImpl);
+
+    if (d->state == QNetworkReplyHttpImplPrivate::Aborted ||
+        d->state == QNetworkReplyHttpImplPrivate::Finished)
+        return;
+
+    // According to the documentation close only stops the download
+    // by closing we can ignore the download part and continue uploading.
+    QNetworkReply::close();
+
+    // call finished which will emit signals
+    // FIXME shouldn't this be emitted Queued?
+    d->error(OperationCanceledError, tr("Operation canceled"));
+    d->finished();
+}
+
+void QNetworkReplyHttpImpl::abort()
+{
+    Q_D(QNetworkReplyHttpImpl);
+    // FIXME
+    if (d->state == QNetworkReplyHttpImplPrivate::Finished || d->state == QNetworkReplyHttpImplPrivate::Aborted)
+        return;
+
+    QNetworkReply::close();
+
+    if (d->state != QNetworkReplyHttpImplPrivate::Finished) {
+        // call finished which will emit signals
+        // FIXME shouldn't this be emitted Queued?
+        d->error(OperationCanceledError, tr("Operation canceled"));
+        d->finished();
+    }
+
+    d->state = QNetworkReplyHttpImplPrivate::Aborted;
+
+    emit abortHttpRequest();
+}
+
+qint64 QNetworkReplyHttpImpl::bytesAvailable() const
+{
+    Q_D(const QNetworkReplyHttpImpl);
+
+    // if we load from cache device
+    if (d->cacheLoadDevice) {
+        return QNetworkReply::bytesAvailable() + d->cacheLoadDevice->bytesAvailable() + d->downloadMultiBuffer.byteAmount();
+    }
+
+    // zerocopy buffer
+    if (d->downloadZerocopyBuffer) {
+        return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
+    }
+
+    // normal buffer
+    return QNetworkReply::bytesAvailable() + d->downloadMultiBuffer.byteAmount();
+}
+
+bool QNetworkReplyHttpImpl::isSequential () const
+{
+    // FIXME In the cache of a cached load or the zero-copy buffer we could actually be non-sequential.
+    // FIXME however this requires us to implement stuff like seek() too.
+    return true;
+}
+
+qint64 QNetworkReplyHttpImpl::size() const
+{
+    // FIXME At some point, this could return a proper value, e.g. if we're non-sequential.
+    return QNetworkReply::size();
+}
+
+qint64 QNetworkReplyHttpImpl::readData(char* data, qint64 maxlen)
+{
+    Q_D(QNetworkReplyHttpImpl);
+
+    // cacheload device
+    if (d->cacheLoadDevice) {
+        // FIXME bytesdownloaded, position etc?
+
+        // There is something already in the buffer we buffered before because the user did not read()
+        // anything, so we read there first:
+        if (!d->downloadMultiBuffer.isEmpty()) {
+            return d->downloadMultiBuffer.read(data, maxlen);
+        }
+
+        qint64 ret = d->cacheLoadDevice->read(data, maxlen);
+        return ret;
+    }
+
+    // zerocopy buffer
+    if (d->downloadZerocopyBuffer) {
+        // FIXME bytesdownloaded, position etc?
+
+        qint64 howMuch = qMin(maxlen, (d->downloadBufferCurrentSize - d->downloadBufferReadPosition));
+        memcpy(data, d->downloadZerocopyBuffer + d->downloadBufferReadPosition, howMuch);
+        d->downloadBufferReadPosition += howMuch;
+        return howMuch;
+
+    }
+
+    // normal buffer
+    if (d->downloadMultiBuffer.isEmpty()) {
+        if (d->state == d->Finished || d->state == d->Aborted)
+            return -1;
+        return 0;
+    }
+
+    if (maxlen == 1) {
+        // optimization for getChar()
+        *data = d->downloadMultiBuffer.getChar();
+        return 1;
+    }
+
+    maxlen = qMin<qint64>(maxlen, d->downloadMultiBuffer.byteAmount());
+    return d->downloadMultiBuffer.read(data, maxlen);
+}
+
+void QNetworkReplyHttpImpl::setReadBufferSize(qint64 size)
+{
+    // FIXME, unsupported right now
+    return;
+}
+
+bool QNetworkReplyHttpImpl::canReadLine () const
+{
+    Q_D(const QNetworkReplyHttpImpl);
+
+    if (QNetworkReply::canReadLine())
+        return true;
+
+    if (d->cacheLoadDevice)
+        return d->cacheLoadDevice->canReadLine() || d->downloadMultiBuffer.canReadLine();
+
+    // FIXME zerocopy buffer?
+
+    return d->downloadMultiBuffer.canReadLine();
+}
+
+#ifndef QT_NO_OPENSSL
+void QNetworkReplyHttpImpl::ignoreSslErrors()
+{
+    Q_D(QNetworkReplyHttpImpl);
+
+    d->pendingIgnoreAllSslErrors = true;
+}
+
+void QNetworkReplyHttpImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
+{
+    Q_D(QNetworkReplyHttpImpl);
+
+    // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
+    // is called before QNetworkAccessManager::get() (or post(), etc.)
+    d->pendingIgnoreSslErrorsList = errors;
+}
+
+void QNetworkReplyHttpImpl::setSslConfigurationImplementation(const QSslConfiguration &newconfig)
+{
+    // Setting a SSL configuration on a reply is not supported. The user needs to set
+    // her/his QSslConfiguration on the QNetworkRequest.
+    Q_UNUSED(newconfig);
+}
+
+QSslConfiguration QNetworkReplyHttpImpl::sslConfigurationImplementation() const
+{
+    Q_D(const QNetworkReplyHttpImpl);
+    return d->sslConfiguration;
+}
+#endif
+
+QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate()
+    : QNetworkReplyPrivate()
+    , manager(0)
+    , managerPrivate(0)
+    , synchronous(false)
+    , state(Idle)
+    , statusCode(0)
+    , outgoingData(0)
+    , bytesUploaded(-1)
+    , cacheLoadDevice(0)
+    , loadingFromCache(false)
+    , cacheSaveDevice(0)
+    , cacheEnabled(false)
+    , resumeOffset(0)
+    , preMigrationDownloaded(-1)
+    , bytesDownloaded(0)
+    , lastBytesDownloaded(-1)
+    , downloadBufferReadPosition(0)
+    , downloadBufferCurrentSize(0)
+    , downloadBufferMaximumSize(0)
+    , downloadZerocopyBuffer(0)
+    , pendingDownloadDataEmissions(new QAtomicInt())
+    , pendingDownloadProgressEmissions(new QAtomicInt())
+    #ifndef QT_NO_OPENSSL
+    , pendingIgnoreAllSslErrors(false)
+    #endif
+
+{
+}
+
+QNetworkReplyHttpImplPrivate::~QNetworkReplyHttpImplPrivate()
+{
+    Q_Q(QNetworkReplyHttpImpl);
+    // This will do nothing if the request was already finished or aborted
+    emit q->abortHttpRequest();
+}
+
+/*
+    For a given httpRequest
+    1) If AlwaysNetwork, return
+    2) If we have a cache entry for this url populate headers so the server can return 304
+    3) Calculate if response_is_fresh and if so send the cache and set loadedFromCache to true
+ */
+bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest)
+{
+    QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
+        (QNetworkRequest::CacheLoadControl)request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
+    if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
+        // If the request does not already specify preferred cache-control
+        // force reload from the network and tell any caching proxy servers to reload too
+        if (!request.rawHeaderList().contains("Cache-Control")) {
+            httpRequest.setHeaderField("Cache-Control", "no-cache");
+            httpRequest.setHeaderField("Pragma", "no-cache");
+        }
+        return false;
+    }
+
+    // The disk cache API does not currently support partial content retrieval.
+    // That is why we don't use the disk cache for any such requests.
+    if (request.hasRawHeader("Range"))
+        return false;
+
+    QAbstractNetworkCache *nc = managerPrivate->networkCache;
+    if (!nc)
+        return false;                 // no local cache
+
+    QNetworkCacheMetaData metaData = nc->metaData(request.url());
+    if (!metaData.isValid())
+        return false;                 // not in cache
+
+    if (!metaData.saveToDisk())
+        return false;
+
+    QNetworkHeadersPrivate cacheHeaders;
+    QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
+    cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
+
+    it = cacheHeaders.findRawHeader("etag");
+    if (it != cacheHeaders.rawHeaders.constEnd())
+        httpRequest.setHeaderField("If-None-Match", it->second);
+
+    QDateTime lastModified = metaData.lastModified();
+    if (lastModified.isValid())
+        httpRequest.setHeaderField("If-Modified-Since", QNetworkHeadersPrivate::toHttpDate(lastModified));
+
+    it = cacheHeaders.findRawHeader("Cache-Control");
+    if (it != cacheHeaders.rawHeaders.constEnd()) {
+        QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
+        if (cacheControl.contains("must-revalidate"))
+            return false;
+    }
+
+    QDateTime currentDateTime = QDateTime::currentDateTime();
+    QDateTime expirationDate = metaData.expirationDate();
+
+#if 0
+    /*
+     * age_value
+     *      is the value of Age: header received by the cache with
+     *              this response.
+     * date_value
+     *      is the value of the origin server's Date: header
+     * request_time
+     *      is the (local) time when the cache made the request
+     *              that resulted in this cached response
+     * response_time
+     *      is the (local) time when the cache received the
+     *              response
+     * now
+     *      is the current (local) time
+     */
+    int age_value = 0;
+    it = cacheHeaders.findRawHeader("age");
+    if (it != cacheHeaders.rawHeaders.constEnd())
+        age_value = it->second.toInt();
+
+    QDateTime dateHeader;
+    int date_value = 0;
+    it = cacheHeaders.findRawHeader("date");
+    if (it != cacheHeaders.rawHeaders.constEnd()) {
+        dateHeader = QNetworkHeadersPrivate::fromHttpDate(it->second);
+        date_value = dateHeader.toTime_t();
+    }
+
+    int now = currentDateTime.toUTC().toTime_t();
+    int request_time = now;
+    int response_time = now;
+
+    // Algorithm from RFC 2616 section 13.2.3
+    int apparent_age = qMax(0, response_time - date_value);
+    int corrected_received_age = qMax(apparent_age, age_value);
+    int response_delay = response_time - request_time;
+    int corrected_initial_age = corrected_received_age + response_delay;
+    int resident_time = now - response_time;
+    int current_age   = corrected_initial_age + resident_time;
+
+    // RFC 2616 13.2.4 Expiration Calculations
+    if (!expirationDate.isValid()) {
+        if (lastModified.isValid()) {
+            int diff = currentDateTime.secsTo(lastModified);
+            expirationDate = lastModified;
+            expirationDate.addSecs(diff / 10);
+            if (httpRequest.headerField("Warning").isEmpty()) {
+                QDateTime dt;
+                dt.setTime_t(current_age);
+                if (dt.daysTo(currentDateTime) > 1)
+                    httpRequest.setHeaderField("Warning", "113");
+            }
+        }
+    }
+
+    // the cache-saving code below sets the expirationDate with date+max_age
+    // if "max-age" is present, or to Expires otherwise
+    int freshness_lifetime = dateHeader.secsTo(expirationDate);
+    bool response_is_fresh = (freshness_lifetime > current_age);
+#else
+    bool response_is_fresh = currentDateTime.secsTo(expirationDate) >= 0;
+#endif
+
+    if (!response_is_fresh)
+        return false;
+
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+    qDebug() << "response_is_fresh" << CacheLoadControlAttribute;
+#endif
+    return sendCacheContents(metaData);
+}
+
+QHttpNetworkRequest::Priority QNetworkReplyHttpImplPrivate::convert(const QNetworkRequest::Priority& prio)
+{
+    switch (prio) {
+    case QNetworkRequest::LowPriority:
+        return QHttpNetworkRequest::LowPriority;
+    case QNetworkRequest::HighPriority:
+        return QHttpNetworkRequest::HighPriority;
+    case QNetworkRequest::NormalPriority:
+    default:
+        return QHttpNetworkRequest::NormalPriority;
+    }
+}
+
+void QNetworkReplyHttpImplPrivate::postRequest()
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    QThread *thread = 0;
+    if (synchronous) {
+        // A synchronous HTTP request uses its own thread
+        thread = new QThread();
+        QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+        thread->start();
+    } else if (!managerPrivate->httpThread) {
+        // We use the manager-global thread.
+        // At some point we could switch to having multiple threads if it makes sense.
+        managerPrivate->httpThread = new QThread();
+        QObject::connect(managerPrivate->httpThread, SIGNAL(finished()), managerPrivate->httpThread, SLOT(deleteLater()));
+        managerPrivate->httpThread->start();
+
+        thread = managerPrivate->httpThread;
+    } else {
+        // Asynchronous request, thread already exists
+        thread = managerPrivate->httpThread;
+    }
+
+    QUrl url = request.url();
+    httpRequest.setUrl(url);
+
+    bool ssl = url.scheme().toLower() == QLatin1String("https");
+    q->setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl);
+    httpRequest.setSsl(ssl);
+
+
+#ifndef QT_NO_NETWORKPROXY
+    QNetworkProxy transparentProxy, cacheProxy;
+
+    // FIXME the proxy stuff should be done in the HTTP thread
+    foreach (const QNetworkProxy &p, managerPrivate->queryProxy(QNetworkProxyQuery(request.url()))) {
+    //foreach (const QNetworkProxy &p, proxyList()) {
+        // use the first proxy that works
+        // for non-encrypted connections, any transparent or HTTP proxy
+        // for encrypted, only transparent proxies
+        if (!ssl
+            && (p.capabilities() & QNetworkProxy::CachingCapability)
+            && (p.type() == QNetworkProxy::HttpProxy ||
+                p.type() == QNetworkProxy::HttpCachingProxy)) {
+            cacheProxy = p;
+            transparentProxy = QNetworkProxy::NoProxy;
+            break;
+        }
+        if (p.isTransparentProxy()) {
+            transparentProxy = p;
+            cacheProxy = QNetworkProxy::NoProxy;
+            break;
+        }
+    }
+
+    // check if at least one of the proxies
+    if (transparentProxy.type() == QNetworkProxy::DefaultProxy &&
+        cacheProxy.type() == QNetworkProxy::DefaultProxy) {
+        // unsuitable proxies
+        QMetaObject::invokeMethod(q, "_q_error", synchronous ? Qt::DirectConnection : Qt::QueuedConnection,
+                                  Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError),
+                                  Q_ARG(QString, q->tr("No suitable proxy found")));
+        QMetaObject::invokeMethod(q, "_q_finished", synchronous ? Qt::DirectConnection : Qt::QueuedConnection);
+        return;
+    }
+#endif
+
+
+    bool loadedFromCache = false;
+    httpRequest.setPriority(convert(request.priority()));
+
+    switch (operation) {
+    case QNetworkAccessManager::GetOperation:
+        httpRequest.setOperation(QHttpNetworkRequest::Get);
+        loadedFromCache = loadFromCacheIfAllowed(httpRequest);
+        break;
+
+    case QNetworkAccessManager::HeadOperation:
+        httpRequest.setOperation(QHttpNetworkRequest::Head);
+        loadedFromCache = loadFromCacheIfAllowed(httpRequest);
+        break;
+
+    case QNetworkAccessManager::PostOperation:
+        invalidateCache();
+        httpRequest.setOperation(QHttpNetworkRequest::Post);
+        createUploadByteDevice();
+        break;
+
+    case QNetworkAccessManager::PutOperation:
+        invalidateCache();
+        httpRequest.setOperation(QHttpNetworkRequest::Put);
+        createUploadByteDevice();
+        break;
+
+    case QNetworkAccessManager::DeleteOperation:
+        invalidateCache();
+        httpRequest.setOperation(QHttpNetworkRequest::Delete);
+        break;
+
+    case QNetworkAccessManager::CustomOperation:
+        invalidateCache(); // for safety reasons, we don't know what the operation does
+        httpRequest.setOperation(QHttpNetworkRequest::Custom);
+        createUploadByteDevice();
+        httpRequest.setCustomVerb(request.attribute(
+                QNetworkRequest::CustomVerbAttribute).toByteArray());
+        break;
+
+    default:
+        break;                  // can't happen
+    }
+
+    if (loadedFromCache) {
+        return;    // no need to send the request! :)
+    }
+
+    QList<QByteArray> headers = request.rawHeaderList();
+    if (resumeOffset != 0) {
+        if (headers.contains("Range")) {
+            // Need to adjust resume offset for user specified range
+
+            headers.removeOne("Range");
+
+            // We've already verified that requestRange starts with "bytes=", see canResume.
+            QByteArray requestRange = request.rawHeader("Range").mid(6);
+
+            int index = requestRange.indexOf('-');
+
+            quint64 requestStartOffset = requestRange.left(index).toULongLong();
+            quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong();
+
+            requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) +
+                           '-' + QByteArray::number(requestEndOffset);
+
+            httpRequest.setHeaderField("Range", requestRange);
+        } else {
+            httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-');
+        }
+    }
+
+    foreach (const QByteArray &header, headers)
+        httpRequest.setHeaderField(header, request.rawHeader(header));
+
+    if (request.attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)
+        httpRequest.setPipeliningAllowed(true);
+
+    if (static_cast<QNetworkRequest::LoadControl>
+        (request.attribute(QNetworkRequest::AuthenticationReuseAttribute,
+                             QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
+        httpRequest.setWithCredentials(false);
+
+
+    // Create the HTTP thread delegate
+    QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
+
+    // For the synchronous HTTP, this is the normal way the delegate gets deleted
+    // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished
+    QObject::connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
+
+    // Set the properties it needs
+    delegate->httpRequest = httpRequest;
+#ifndef QT_NO_NETWORKPROXY
+    delegate->cacheProxy = cacheProxy;
+    delegate->transparentProxy = transparentProxy;
+#endif
+    delegate->ssl = ssl;
+#ifndef QT_NO_OPENSSL
+    if (ssl)
+        delegate->incomingSslConfiguration = request.sslConfiguration();
+#endif
+
+    // Do we use synchronous HTTP?
+    delegate->synchronous = synchronous;
+
+    // The authentication manager is used to avoid the BlockingQueuedConnection communication
+    // from HTTP thread to user thread in some cases.
+    delegate->authenticationManager = managerPrivate->authenticationManager;
+
+    if (!synchronous) {
+        // Tell our zerocopy policy to the delegate
+        delegate->downloadBufferMaximumSize =
+                request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong();
+
+        // These atomic integers are used for signal compression
+        delegate->pendingDownloadData = pendingDownloadDataEmissions;
+        delegate->pendingDownloadProgress = pendingDownloadProgressEmissions;
+
+        // Connect the signals of the delegate to us
+        QObject::connect(delegate, SIGNAL(downloadData(QByteArray)),
+                q, SLOT(replyDownloadData(QByteArray)),
+                Qt::QueuedConnection);
+        QObject::connect(delegate, SIGNAL(downloadFinished()),
+                q, SLOT(replyFinished()),
+                Qt::QueuedConnection);
+        QObject::connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
+                q, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
+                Qt::QueuedConnection);
+        QObject::connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
+                q, SLOT(replyDownloadProgressSlot(qint64,qint64)),
+                Qt::QueuedConnection);
+        QObject::connect(delegate, SIGNAL(error(QNetworkReply::NetworkError,QString)),
+                q, SLOT(httpError(QNetworkReply::NetworkError, const QString)),
+                Qt::QueuedConnection);
+#ifndef QT_NO_OPENSSL
+        QObject::connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
+                q, SLOT(replySslConfigurationChanged(QSslConfiguration)),
+                Qt::QueuedConnection);
+#endif
+        // Those need to report back, therefire BlockingQueuedConnection
+        QObject::connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+                q, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+                Qt::BlockingQueuedConnection);
+#ifndef QT_NO_NETWORKPROXY
+        QObject::connect(delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+                 q, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+                 Qt::BlockingQueuedConnection);
+#endif
+#ifndef QT_NO_OPENSSL
+        QObject::connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
+                q, SLOT(replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *)),
+                Qt::BlockingQueuedConnection);
+#endif
+        // This signal we will use to start the request.
+        QObject::connect(q, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));
+        QObject::connect(q, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));
+
+        if (uploadByteDevice) {
+            QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice =
+                    new QNonContiguousByteDeviceThreadForwardImpl(uploadByteDevice->atEnd(), uploadByteDevice->size());
+            if (uploadByteDevice->isResetDisabled())
+                forwardUploadDevice->disableReset();
+            forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread()
+            delegate->httpRequest.setUploadByteDevice(forwardUploadDevice);
+
+            // From main thread to user thread:
+            QObject::connect(q, SIGNAL(haveUploadData(QByteArray, bool, qint64)),
+                             forwardUploadDevice, SLOT(haveDataSlot(QByteArray, bool, qint64)), Qt::QueuedConnection);
+            QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()),
+                             forwardUploadDevice, SIGNAL(readyRead()),
+                             Qt::QueuedConnection);
+
+            // From http thread to user thread:
+            QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),
+                             q, SLOT(wantUploadDataSlot(qint64)));
+            QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)),
+                             q, SLOT(sentUploadDataSlot(qint64)));
+            QObject::connect(forwardUploadDevice, SIGNAL(resetData(bool*)),
+                    q, SLOT(resetUploadDataSlot(bool*)),
+                    Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued!
+        }
+    } else if (synchronous) {
+        QObject::connect(q, SIGNAL(startHttpRequestSynchronously()), delegate, SLOT(startRequestSynchronously()), Qt::BlockingQueuedConnection);
+
+        if (uploadByteDevice) {
+            // For the synchronous HTTP use case the use thread (this one here) is blocked
+            // so we cannot use the asynchronous upload architecture.
+            // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly
+            // use the uploadByteDevice provided to us by the QNetworkReplyImpl.
+            // The code that is in start() makes sure it is safe to use from a thread
+            // since it only wraps a QRingBuffer
+            delegate->httpRequest.setUploadByteDevice(uploadByteDevice.data());
+        }
+    }
+
+
+    // Move the delegate to the http thread
+    delegate->moveToThread(thread);
+    // This call automatically moves the uploadDevice too for the asynchronous case.
+
+    // Send an signal to the delegate so it starts working in the other thread
+    if (synchronous) {
+        emit q->startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
+
+        if (delegate->incomingErrorCode != QNetworkReply::NoError) {
+            replyDownloadMetaData
+                    (delegate->incomingHeaders,
+                     delegate->incomingStatusCode,
+                     delegate->incomingReasonPhrase,
+                     delegate->isPipeliningUsed,
+                     QSharedPointer<char>(),
+                     delegate->incomingContentLength);
+            httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
+        } else {
+            replyDownloadMetaData
+                    (delegate->incomingHeaders,
+                     delegate->incomingStatusCode,
+                     delegate->incomingReasonPhrase,
+                     delegate->isPipeliningUsed,
+                     QSharedPointer<char>(),
+                     delegate->incomingContentLength);
+            replyDownloadData(delegate->synchronousDownloadData);
+        }
+
+        // End the thread. It will delete itself from the finished() signal
+        thread->quit();
+
+        finished();
+    } else {
+        emit q->startHttpRequest(); // Signal to the HTTP thread and go back to user.
+    }
+}
+
+void QNetworkReplyHttpImplPrivate::invalidateCache()
+{
+    QAbstractNetworkCache *nc = managerPrivate->networkCache;
+    if (nc)
+        nc->remove(request.url());
+}
+
+void QNetworkReplyHttpImplPrivate::initCacheSaveDevice()
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    // The disk cache does not support partial content, so don't even try to
+    // save any such content into the cache.
+    if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
+        cacheEnabled = false;
+        return;
+    }
+
+    // save the meta data
+    QNetworkCacheMetaData metaData;
+    metaData.setUrl(url);
+    metaData = fetchCacheMetaData(metaData);
+
+    // save the redirect request also in the cache
+    QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
+    if (redirectionTarget.isValid()) {
+        QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
+        attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
+        metaData.setAttributes(attributes);
+    }
+
+    cacheSaveDevice = managerPrivate->networkCache->prepare(metaData);
+
+    if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
+        if (cacheSaveDevice && !cacheSaveDevice->isOpen())
+            qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
+                  "class %s probably needs to be fixed",
+                  managerPrivate->networkCache->metaObject()->className());
+
+        managerPrivate->networkCache->remove(url);
+        cacheSaveDevice = 0;
+        cacheEnabled = false;
+    }
+}
+
+void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    // If we're closed just ignore this data
+    if (!q->isOpen())
+        return;
+
+    int pendingSignals = (int)pendingDownloadDataEmissions->fetchAndAddAcquire(-1) - 1;
+
+    if (pendingSignals > 0) {
+        // Some more signal emissions to this slot are pending.
+        // Instead of writing the downstream data, we wait
+        // and do it in the next call we get
+        // (signal comppression)
+        pendingDownloadData.append(d);
+        return;
+    }
+
+    pendingDownloadData.append(d);
+    d.clear();
+    // We need to usa a copy for calling writeDownstreamData as we could
+    // possibly recurse into this this function when we call
+    // appendDownstreamDataSignalEmissions because the user might call
+    // processEvents() or spin an event loop when this occur.
+    QByteDataBuffer pendingDownloadDataCopy = pendingDownloadData;
+    pendingDownloadData.clear();
+
+    if (cacheEnabled && !cacheSaveDevice) {
+        initCacheSaveDevice();
+    }
+
+    qint64 bytesWritten = 0;
+    for (int i = 0; i < pendingDownloadDataCopy.bufferCount(); i++) {
+        QByteArray const &item = pendingDownloadDataCopy[i];
+
+        if (cacheSaveDevice)
+            cacheSaveDevice->write(item.constData(), item.size());
+        downloadMultiBuffer.append(item);
+
+        bytesWritten += item.size();
+    }
+    pendingDownloadDataCopy.clear();
+
+    bytesDownloaded += bytesWritten;
+    lastBytesDownloaded = bytesDownloaded;
+
+
+    QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+    if (preMigrationDownloaded != Q_INT64_C(-1))
+        totalSize = totalSize.toLongLong() + preMigrationDownloaded;
+
+    emit q->readyRead();
+    // emit readyRead before downloadProgress incase this will cause events to be
+    // processed and we get into a recursive call (as in QProgressDialog).
+    emit q->downloadProgress(bytesDownloaded,
+                             totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+
+}
+
+void QNetworkReplyHttpImplPrivate::replyFinished()
+{
+    // We are already loading from cache, we still however
+    // got this signal because it was posted already
+    if (loadingFromCache)
+        return;
+
+    finished();
+}
+
+void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
+{
+    Q_Q(QNetworkReplyHttpImpl);
+    switch (statusCode) {
+    case 301:                   // Moved Permanently
+    case 302:                   // Found
+    case 303:                   // See Other
+    case 307:                   // Temporary Redirect
+        // What do we do about the caching of the HTML note?
+        // The response to a 303 MUST NOT be cached, while the response to
+        // all of the others is cacheable if the headers indicate it to be
+        QByteArray header = q->rawHeader("location");
+        QUrl url = QUrl::fromEncoded(header);
+        if (!url.isValid())
+            url = QUrl(QLatin1String(header));
+        // FIXME?
+        //redirectionRequested(url);
+        q->setAttribute(QNetworkRequest::RedirectionTargetAttribute, url);
+    }
+}
+
+void QNetworkReplyHttpImplPrivate::replyDownloadMetaData
+        (QList<QPair<QByteArray,QByteArray> > hm,
+         int sc,QString rp,bool pu,
+         QSharedPointer<char> db,
+         qint64 contentLength)
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    statusCode = sc;
+    reasonPhrase = rp;
+
+    // Download buffer
+    if (!db.isNull()) {
+        //setDownloadBuffer(db, contentLength);
+        downloadBufferPointer = db;
+        downloadZerocopyBuffer = downloadBufferPointer.data();
+        downloadBufferCurrentSize = 0;
+        downloadBufferMaximumSize = contentLength;
+        q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
+    }
+
+    q->setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu);
+
+    // reconstruct the HTTP header
+    QList<QPair<QByteArray, QByteArray> > headerMap = hm;
+    QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(),
+                                                        end = headerMap.constEnd();
+    QByteArray header;
+
+    for (; it != end; ++it) {
+        QByteArray value = q->rawHeader(it->first);
+        if (!value.isEmpty()) {
+            if (qstricmp(it->first.constData(), "set-cookie") == 0)
+                value += '\n';
+            else
+                value += ", ";
+        }
+        value += it->second;
+        q->setRawHeader(it->first, value);
+    }
+
+    q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
+    q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
+
+    // is it a redirection?
+    checkForRedirect(statusCode);
+
+    if (statusCode >= 500 && statusCode < 600) {
+        QAbstractNetworkCache *nc = managerPrivate->networkCache;
+        if (nc) {
+            QNetworkCacheMetaData metaData = nc->metaData(request.url());
+            QNetworkHeadersPrivate cacheHeaders;
+            cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
+            QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
+            it = cacheHeaders.findRawHeader("Cache-Control");
+            bool mustReValidate = false;
+            if (it != cacheHeaders.rawHeaders.constEnd()) {
+                QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
+                if (cacheControl.contains("must-revalidate"))
+                    mustReValidate = true;
+            }
+            if (!mustReValidate && sendCacheContents(metaData))
+                return;
+        }
+    }
+
+    if (statusCode == 304) {
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+        qDebug() << "Received a 304 from" << url();
+#endif
+        QAbstractNetworkCache *nc = managerPrivate->networkCache;
+        if (nc) {
+            QNetworkCacheMetaData oldMetaData = nc->metaData(request.url());
+            QNetworkCacheMetaData metaData = fetchCacheMetaData(oldMetaData);
+            if (oldMetaData != metaData)
+                nc->updateMetaData(metaData);
+            if (sendCacheContents(metaData))
+                return;
+        }
+    }
+
+
+    if (statusCode != 304 && statusCode != 303) {
+        if (!isCachingEnabled())
+            setCachingEnabled(true);
+    }
+
+    metaDataChanged();
+}
+
+void QNetworkReplyHttpImplPrivate::replyDownloadProgressSlot(qint64 bytesReceived,  qint64 bytesTotal)
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    // If we're closed just ignore this data
+    if (!q->isOpen())
+        return;
+
+    // we can be sure here that there is a download buffer
+
+    int pendingSignals = (int)pendingDownloadProgressEmissions->fetchAndAddAcquire(-1) - 1;
+    if (pendingSignals > 0) {
+        // Let's ignore this signal and look at the next one coming in
+        // (signal comppression)
+        return;
+    }
+
+    if (!q->isOpen())
+        return;
+
+    if (cacheEnabled && bytesReceived == bytesTotal) {
+        // Write everything in one go if we use a download buffer. might be more performant.
+        initCacheSaveDevice();
+        // need to check again if cache enabled and device exists
+        if (cacheSaveDevice && cacheEnabled)
+            cacheSaveDevice->write(downloadZerocopyBuffer, bytesTotal);
+        // FIXME where is it closed?
+    }
+
+    bytesDownloaded = bytesReceived;
+    lastBytesDownloaded = bytesReceived;
+
+    downloadBufferCurrentSize = bytesReceived;
+
+    // Only emit readyRead when actual data is there
+    // emit readyRead before downloadProgress incase this will cause events to be
+    // processed and we get into a recursive call (as in QProgressDialog).
+    if (bytesDownloaded > 0)
+        emit q->readyRead();
+    emit q->downloadProgress(bytesDownloaded, bytesTotal);
+}
+
+void QNetworkReplyHttpImplPrivate::httpAuthenticationRequired(const QHttpNetworkRequest &,
+                                                           QAuthenticator *auth)
+{
+    Q_Q(QNetworkReplyHttpImpl);
+    managerPrivate->authenticationRequired(auth, q_func(), synchronous, url, &urlForLastAuthentication);
+}
+
+#ifndef QT_NO_NETWORKPROXY
+void QNetworkReplyHttpImplPrivate::proxyAuthenticationRequired(const QNetworkProxy &proxy,
+                                                        QAuthenticator *authenticator)
+{
+    managerPrivate->proxyAuthenticationRequired(proxy, synchronous, authenticator, &lastProxyAuthentication);
+}
+#endif
+
+void QNetworkReplyHttpImplPrivate::httpError(QNetworkReply::NetworkError errorCode,
+                                          const QString &errorString)
+{
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+    qDebug() << "http error!" << errorCode << errorString;
+#endif
+
+    // FIXME?
+    error(errorCode, errorString);
+}
+
+#ifndef QT_NO_OPENSSL
+void QNetworkReplyHttpImplPrivate::replySslErrors(
+        const QList<QSslError> &list, bool *ignoreAll, QList<QSslError> *toBeIgnored)
+{
+    Q_Q(QNetworkReplyHttpImpl);
+    emit q->sslErrors(list);
+    // Check if the callback set any ignore and return this here to http thread
+    if (pendingIgnoreAllSslErrors)
+        *ignoreAll = true;
+    if (!pendingIgnoreSslErrorsList.isEmpty())
+        *toBeIgnored = pendingIgnoreSslErrorsList;
+}
+
+void QNetworkReplyHttpImplPrivate::replySslConfigurationChanged(const QSslConfiguration &sslConfiguration)
+{
+    // Receiving the used SSL configuration from the HTTP thread
+    this->sslConfiguration = sslConfiguration;
+}
+#endif
+
+// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
+void QNetworkReplyHttpImplPrivate::resetUploadDataSlot(bool *r)
+{
+    *r = uploadByteDevice->reset();
+}
+
+// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
+void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 amount)
+{
+    uploadByteDevice->advanceReadPointer(amount);
+}
+
+// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
+void QNetworkReplyHttpImplPrivate::wantUploadDataSlot(qint64 maxSize)
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    // call readPointer
+    qint64 currentUploadDataLength = 0;
+    char *data = const_cast<char*>(uploadByteDevice->readPointer(maxSize, currentUploadDataLength));
+    // Let's make a copy of this data
+    QByteArray dataArray(data, currentUploadDataLength);
+
+    // Communicate back to HTTP thread
+    emit q->haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size());
+}
+
+/*
+    A simple web page that can be used to test us: http://www.procata.com/cachetest/
+ */
+bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData &metaData)
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    setCachingEnabled(false);
+    if (!metaData.isValid())
+        return false;
+
+    QAbstractNetworkCache *nc = managerPrivate->networkCache;
+    Q_ASSERT(nc);
+    QIODevice *contents = nc->data(url);
+    if (!contents) {
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+        qDebug() << "Can not send cache, the contents are 0" << url;
+#endif
+        return false;
+    }
+    contents->setParent(q);
+
+    QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
+    int status = attributes.value(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+    if (status < 100)
+        status = 200;           // fake it
+
+    q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
+    q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
+    q->setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
+
+    QNetworkCacheMetaData::RawHeaderList rawHeaders = metaData.rawHeaders();
+    QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(),
+                                                       end = rawHeaders.constEnd();
+    for ( ; it != end; ++it)
+        setRawHeader(it->first, it->second);
+
+    checkForRedirect(status);
+
+    cacheLoadDevice = contents;
+    q->connect(cacheLoadDevice, SIGNAL(readyRead()), SLOT(_q_cacheLoadReadyRead()));
+    q->connect(cacheLoadDevice, SIGNAL(readChannelFinished()), SLOT(_q_cacheLoadReadyRead()));
+
+    // This needs to be emitted in the event loop because it can be reached at
+    // the direct code path of qnam.get(...) before the user has a chance
+    // to connect any signals.
+    QMetaObject::invokeMethod(q, "metaDataChanged", Qt::QueuedConnection);
+    QMetaObject::invokeMethod(q, "_q_cacheLoadReadyRead", Qt::QueuedConnection);
+
+
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+    qDebug() << "Successfully sent cache:" << url << contents->size() << "bytes";
+#endif
+
+    // Set the following flag so we can ignore some signals from HTTP thread
+    // that would still come
+    loadingFromCache = true;
+    return true;
+}
+
+QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNetworkCacheMetaData &oldMetaData) const
+{
+    Q_Q(const QNetworkReplyHttpImpl);
+
+    QNetworkCacheMetaData metaData = oldMetaData;
+
+    QNetworkHeadersPrivate cacheHeaders;
+    cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
+    QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
+
+    QList<QByteArray> newHeaders = q->rawHeaderList();
+    foreach (QByteArray header, newHeaders) {
+        QByteArray originalHeader = header;
+        header = header.toLower();
+        bool hop_by_hop =
+            (header == "connection"
+             || header == "keep-alive"
+             || header == "proxy-authenticate"
+             || header == "proxy-authorization"
+             || header == "te"
+             || header == "trailers"
+             || header == "transfer-encoding"
+             || header ==  "upgrade");
+        if (hop_by_hop)
+            continue;
+
+        // for 4.6.0, we were planning to not store the date header in the
+        // cached resource; through that we planned to reduce the number
+        // of writes to disk when using a QNetworkDiskCache (i.e. don't
+        // write to disk when only the date changes).
+        // However, without the date we cannot calculate the age of the page
+        // anymore.
+        //if (header == "date")
+            //continue;
+
+        // Don't store Warning 1xx headers
+        if (header == "warning") {
+            QByteArray v = q->rawHeader(header);
+            if (v.length() == 3
+                && v[0] == '1'
+                && v[1] >= '0' && v[1] <= '9'
+                && v[2] >= '0' && v[2] <= '9')
+                continue;
+        }
+
+        it = cacheHeaders.findRawHeader(header);
+        if (it != cacheHeaders.rawHeaders.constEnd()) {
+            // Match the behavior of Firefox and assume Cache-Control: "no-transform"
+            if (header == "content-encoding"
+                || header == "content-range"
+                || header == "content-type")
+                continue;
+
+            // For MS servers that send "Content-Length: 0" on 304 responses
+            // ignore this too
+            if (header == "content-length")
+                continue;
+        }
+
+#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
+        QByteArray n = rawHeader(header);
+        QByteArray o;
+        if (it != cacheHeaders.rawHeaders.constEnd())
+            o = (*it).second;
+        if (n != o && header != "date") {
+            qDebug() << "replacing" << header;
+            qDebug() << "new" << n;
+            qDebug() << "old" << o;
+        }
+#endif
+        cacheHeaders.setRawHeader(originalHeader, q->rawHeader(header));
+    }
+    metaData.setRawHeaders(cacheHeaders.rawHeaders);
+
+    bool checkExpired = true;
+
+    QHash<QByteArray, QByteArray> cacheControl;
+    it = cacheHeaders.findRawHeader("Cache-Control");
+    if (it != cacheHeaders.rawHeaders.constEnd()) {
+        cacheControl = parseHttpOptionHeader(it->second);
+        QByteArray maxAge = cacheControl.value("max-age");
+        if (!maxAge.isEmpty()) {
+            checkExpired = false;
+            QDateTime dt = QDateTime::currentDateTime();
+            dt = dt.addSecs(maxAge.toInt());
+            metaData.setExpirationDate(dt);
+        }
+    }
+    if (checkExpired) {
+        it = cacheHeaders.findRawHeader("expires");
+        if (it != cacheHeaders.rawHeaders.constEnd()) {
+            QDateTime expiredDateTime = QNetworkHeadersPrivate::fromHttpDate(it->second);
+            metaData.setExpirationDate(expiredDateTime);
+        }
+    }
+
+    it = cacheHeaders.findRawHeader("last-modified");
+    if (it != cacheHeaders.rawHeaders.constEnd())
+        metaData.setLastModified(QNetworkHeadersPrivate::fromHttpDate(it->second));
+
+    bool canDiskCache;
+    // only cache GET replies by default, all other replies (POST, PUT, DELETE)
+    //  are not cacheable by default (according to RFC 2616 section 9)
+    if (httpRequest.operation() == QHttpNetworkRequest::Get) {
+
+        canDiskCache = true;
+        // 14.32
+        // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client
+        // had sent "Cache-Control: no-cache".
+        it = cacheHeaders.findRawHeader("pragma");
+        if (it != cacheHeaders.rawHeaders.constEnd()
+            && it->second == "no-cache")
+            canDiskCache = false;
+
+        // HTTP/1.1. Check the Cache-Control header
+        if (cacheControl.contains("no-cache"))
+            canDiskCache = false;
+        else if (cacheControl.contains("no-store"))
+            canDiskCache = false;
+
+    // responses to POST might be cacheable
+    } else if (httpRequest.operation() == QHttpNetworkRequest::Post) {
+
+        canDiskCache = false;
+        // some pages contain "expires:" and "cache-control: no-cache" field,
+        // so we only might cache POST requests if we get "cache-control: max-age ..."
+        if (cacheControl.contains("max-age"))
+            canDiskCache = true;
+
+    // responses to PUT and DELETE are not cacheable
+    } else {
+        canDiskCache = false;
+    }
+
+    metaData.setSaveToDisk(canDiskCache);
+    QNetworkCacheMetaData::AttributesMap attributes;
+    if (statusCode != 304) {
+        // update the status code
+        attributes.insert(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
+        attributes.insert(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
+    } else {
+        // this is a redirection, keep the attributes intact
+        attributes = oldMetaData.attributes();
+    }
+    metaData.setAttributes(attributes);
+    return metaData;
+}
+
+bool QNetworkReplyHttpImplPrivate::canResume() const
+{
+    Q_Q(const QNetworkReplyHttpImpl);
+
+    // Only GET operation supports resuming.
+    if (operation != QNetworkAccessManager::GetOperation)
+        return false;
+
+    // Can only resume if server/resource supports Range header.
+    QByteArray acceptRangesheaderName("Accept-Ranges");
+    if (!q->hasRawHeader(acceptRangesheaderName) || q->rawHeader(acceptRangesheaderName) == "none")
+        return false;
+
+    // We only support resuming for byte ranges.
+    if (request.hasRawHeader("Range")) {
+        QByteArray range = request.rawHeader("Range");
+        if (!range.startsWith("bytes="))
+            return false;
+    }
+
+    // If we're using a download buffer then we don't support resuming/migration
+    // right now. Too much trouble.
+    if (downloadZerocopyBuffer)
+        return false;
+
+    return true;
+}
+
+void QNetworkReplyHttpImplPrivate::setResumeOffset(quint64 offset)
+{
+    resumeOffset = offset;
+}
+
+/*!
+    Starts the backend.  Returns true if the backend is started.  Returns false if the backend
+    could not be started due to an unopened or roaming session.  The caller should recall this
+    function once the session has been opened or the roaming process has finished.
+*/
+bool QNetworkReplyHttpImplPrivate::start()
+{
+    if (!managerPrivate->networkSession) {
+        postRequest();
+        return true;
+    }
+
+    // This is not ideal.
+    const QString host = url.host();
+    if (host == QLatin1String("localhost") ||
+        QHostAddress(host) == QHostAddress::LocalHost ||
+        QHostAddress(host) == QHostAddress::LocalHostIPv6) {
+        // Don't need an open session for localhost access.
+        postRequest();
+        return true;
+    }
+
+    if (managerPrivate->networkSession->isOpen() &&
+        managerPrivate->networkSession->state() == QNetworkSession::Connected) {
+        postRequest();
+        return true;
+    }
+
+    return false;
+}
+
+void QNetworkReplyHttpImplPrivate::_q_startOperation()
+{
+    // ensure this function is only being called once
+    if (state == Working) {
+        qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
+        return;
+    }
+    state = Working;
+
+#ifndef QT_NO_BEARERMANAGEMENT
+    if (!start()) { // ### we should call that method even if bearer is not used
+        // backend failed to start because the session state is not Connected.
+        // QNetworkAccessManager will call reply->backend->start() again for us when the session
+        // state changes.
+        state = WaitingForSession;
+
+        QNetworkSession *session = managerPrivate->networkSession.data();
+
+        if (session) {
+            Q_Q(QNetworkReplyHttpImpl);
+
+            QObject::connect(session, SIGNAL(error(QNetworkSession::SessionError)),
+                             q, SLOT(_q_networkSessionFailed()));
+
+            if (!session->isOpen())
+                session->open();
+        } else {
+            qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
+        }
+
+        return;
+    }
+#endif
+
+    if (synchronous) {
+        state = Finished;
+        q_func()->setFinished(true);
+    } else {
+        if (state != Finished) {
+//            if (operation == QNetworkAccessManager::GetOperation)
+//                pendingNotifications.append(NotifyDownstreamReadyWrite);
+
+//            handleNotifications();
+
+        }
+    }
+}
+
+void QNetworkReplyHttpImplPrivate::_q_cacheLoadReadyRead()
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    if (state != Working)
+        return;
+    if (!cacheLoadDevice || !q->isOpen() || !cacheLoadDevice->bytesAvailable())
+        return;
+
+    // FIXME Optimize to use zerocopy download buffer if it is a QBuffer.
+    // Needs to be done where sendCacheContents() (?) of HTTP is emitting
+    // metaDataChanged ?
+
+
+    // FIXME
+    lastBytesDownloaded = bytesDownloaded;
+    QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+
+    //pauseNotificationHandling();
+    // emit readyRead before downloadProgress incase this will cause events to be
+    // processed and we get into a recursive call (as in QProgressDialog).
+
+    // This readyRead() goes to the user. The user then may or may not read() anything.
+    emit q->readyRead();
+    emit q->downloadProgress(bytesDownloaded,
+                             totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+
+    // If there are still bytes available in the cacheLoadDevice then the user did not read
+    // in response to the readyRead() signal. This means we have to load from the cacheLoadDevice
+    // and buffer that stuff. This is needed to be able to properly emit finished() later.
+    while (cacheLoadDevice->bytesAvailable()) {
+        downloadMultiBuffer.append(cacheLoadDevice->readAll());
+    }
+
+    if (cacheLoadDevice->isSequential()) {
+        // check if end and we can read the EOF -1
+        char c;
+        qint64 actualCount = cacheLoadDevice->read(&c, 1);
+        if (actualCount < 0) {
+            cacheLoadDevice->deleteLater();
+            cacheLoadDevice = 0;
+            QMetaObject::invokeMethod(q, "_q_finished", Qt::QueuedConnection);
+        } else if (actualCount == 1) {
+            // This is most probably not happening since most QIODevice returned something proper for bytesAvailable()
+            // and had already been "emptied".
+            cacheLoadDevice->ungetChar(c);
+        }
+    } else if ((!cacheLoadDevice->isSequential() && cacheLoadDevice->atEnd())) {
+        // This codepath is in case the cache device is a QBuffer, e.g. from QNetworkDiskCache.
+        cacheLoadDevice->deleteLater();
+        cacheLoadDevice = 0;
+        QMetaObject::invokeMethod(q, "_q_finished", Qt::QueuedConnection);
+    }
+
+}
+
+
+void QNetworkReplyHttpImplPrivate::_q_bufferOutgoingDataFinished()
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    // make sure this is only called once, ever.
+    //_q_bufferOutgoingData may call it or the readChannelFinished emission
+    if (state != Buffering)
+        return;
+
+    // disconnect signals
+    QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
+    QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
+
+    // finally, start the request
+    QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
+}
+
+void QNetworkReplyHttpImplPrivate::_q_bufferOutgoingData()
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    if (!outgoingDataBuffer) {
+        // first call, create our buffer
+        outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
+
+        QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
+        QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
+    }
+
+    qint64 bytesBuffered = 0;
+    qint64 bytesToBuffer = 0;
+
+    // read data into our buffer
+    forever {
+        bytesToBuffer = outgoingData->bytesAvailable();
+        // unknown? just try 2 kB, this also ensures we always try to read the EOF
+        if (bytesToBuffer <= 0)
+            bytesToBuffer = 2*1024;
+
+        char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
+        bytesBuffered = outgoingData->read(dst, bytesToBuffer);
+
+        if (bytesBuffered == -1) {
+            // EOF has been reached.
+            outgoingDataBuffer->chop(bytesToBuffer);
+
+            _q_bufferOutgoingDataFinished();
+            break;
+        } else if (bytesBuffered == 0) {
+            // nothing read right now, just wait until we get called again
+            outgoingDataBuffer->chop(bytesToBuffer);
+
+            break;
+        } else {
+            // don't break, try to read() again
+            outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
+        }
+    }
+}
+
+#ifndef QT_NO_BEARERMANAGEMENT
+void QNetworkReplyHttpImplPrivate::_q_networkSessionConnected()
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    if (!manager)
+        return;
+
+    QNetworkSession *session = managerPrivate->networkSession.data();
+    if (!session)
+        return;
+
+    if (session->state() != QNetworkSession::Connected)
+        return;
+
+    switch (state) {
+    case QNetworkReplyImplPrivate::Buffering:
+    case QNetworkReplyImplPrivate::Working:
+    case QNetworkReplyImplPrivate::Reconnecting:
+        // Migrate existing downloads to new network connection.
+        migrateBackend();
+        break;
+    case QNetworkReplyImplPrivate::WaitingForSession:
+        // Start waiting requests.
+        QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
+        break;
+    default:
+        ;
+    }
+}
+
+void QNetworkReplyHttpImplPrivate::_q_networkSessionFailed()
+{
+    // Abort waiting and working replies.
+    if (state == WaitingForSession || state == Working) {
+        state = Working;
+        error(QNetworkReplyImpl::UnknownNetworkError,
+              QCoreApplication::translate("QNetworkReply", "Network session error."));
+        finished();
+    }
+}
+#endif
+
+
+// need to have this function since the reply is a private member variable
+// and the special backends need to access this.
+void QNetworkReplyHttpImplPrivate::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
+{
+    Q_Q(QNetworkReplyHttpImpl);
+    if (isFinished)
+        return;
+    emit q->uploadProgress(bytesSent, bytesTotal);
+}
+
+QNonContiguousByteDevice* QNetworkReplyHttpImplPrivate::createUploadByteDevice()
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    if (outgoingDataBuffer)
+        uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(outgoingDataBuffer));
+    else if (outgoingData) {
+        uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(outgoingData));
+    } else {
+        return 0;
+    }
+
+    bool bufferDisallowed =
+            request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
+                          QVariant(false)) == QVariant(true);
+    if (bufferDisallowed)
+        uploadByteDevice->disableReset();
+
+    // We want signal emissions only for normal asynchronous uploads
+    if (!synchronous)
+        QObject::connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)),
+                         q, SLOT(emitReplyUploadProgress(qint64,qint64)));
+
+    return uploadByteDevice.data();
+}
+
+void QNetworkReplyHttpImplPrivate::_q_finished()
+{
+    // This gets called queued, just forward to real call then
+    finished();
+}
+
+void QNetworkReplyHttpImplPrivate::finished()
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    if (state == Finished || state == Aborted || state == WaitingForSession)
+        return;
+
+    //pauseNotificationHandling();
+    QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+    if (preMigrationDownloaded != Q_INT64_C(-1))
+        totalSize = totalSize.toLongLong() + preMigrationDownloaded;
+
+    // FIXME why should it be 0
+    if (manager) {
+#ifndef QT_NO_BEARERMANAGEMENT
+        QNetworkSession *session = managerPrivate->networkSession.data();
+        if (session && session->state() == QNetworkSession::Roaming &&
+            state == Working && errorCode != QNetworkReply::OperationCanceledError) {
+            // only content with a known size will fail with a temporary network failure error
+            if (!totalSize.isNull()) {
+                if (bytesDownloaded != totalSize) {
+                    if (migrateBackend()) {
+                        // either we are migrating or the request is finished/aborted
+                        if (state == Reconnecting || state == WaitingForSession) {
+                            //resumeNotificationHandling();
+                            return; // exit early if we are migrating.
+                        }
+                    } else {
+                        error(QNetworkReply::TemporaryNetworkFailureError,
+                              QNetworkReply::tr("Temporary network failure."));
+                    }
+                }
+            }
+        }
+#endif
+    }
+    //resumeNotificationHandling();
+
+    state = Finished;
+    q->setFinished(true);
+
+    //pendingNotifications.clear();
+
+    //pauseNotificationHandling();
+    if (totalSize.isNull() || totalSize == -1) {
+        emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
+    }
+
+    if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
+        emit q->uploadProgress(0, 0);
+    //resumeNotificationHandling();
+
+    // if we don't know the total size of or we received everything save the cache
+    if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
+        completeCacheSave();
+
+    // note: might not be a good idea, since users could decide to delete us
+    // which would delete the backend too...
+    // maybe we should protect the backend
+    //pauseNotificationHandling();
+    emit q->readChannelFinished();
+    emit q->finished();
+    //resumeNotificationHandling();
+}
+
+void QNetworkReplyHttpImplPrivate::_q_error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
+{
+    this->error(code, errorMessage);
+}
+
+
+void QNetworkReplyHttpImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
+{
+    Q_Q(QNetworkReplyHttpImpl);
+    // Can't set and emit multiple errors.
+    if (errorCode != QNetworkReply::NoError) {
+        qWarning() << "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.";
+        return;
+    }
+
+    errorCode = code;
+    q->setErrorString(errorMessage);
+
+    // note: might not be a good idea, since users could decide to delete us
+    // which would delete the backend too...
+    // maybe we should protect the backend
+    emit q->error(code);
+}
+
+void QNetworkReplyHttpImplPrivate::metaDataChanged()
+{
+    // FIXME merge this with replyDownloadMetaData(); ?
+
+    Q_Q(QNetworkReplyHttpImpl);
+    // 1. do we have cookies?
+    // 2. are we allowed to set them?
+    if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && manager
+        && (static_cast<QNetworkRequest::LoadControl>
+            (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
+                               QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
+        QList<QNetworkCookie> cookies =
+            qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
+        QNetworkCookieJar *jar = manager->cookieJar();
+        if (jar)
+            jar->setCookiesFromUrl(cookies, url);
+    }
+    emit q->metaDataChanged();
+}
+
+/*
+    Migrates the backend of the QNetworkReply to a new network connection if required.  Returns
+    true if the reply is migrated or it is not required; otherwise returns false.
+*/
+bool QNetworkReplyHttpImplPrivate::migrateBackend()
+{
+    Q_Q(QNetworkReplyHttpImpl);
+
+    // Network reply is already finished or aborted, don't need to migrate.
+    if (state == Finished || state == Aborted)
+        return true;
+
+    // Backend does not support resuming download.
+    if (!canResume())
+        return false;
+
+    // Request has outgoing data, not migrating.
+    if (outgoingData)
+        return false;
+
+    // Request is serviced from the cache, don't need to migrate.
+    if (cacheLoadDevice)
+        return true;
+
+    state = Reconnecting;
+
+//    if (backend) {
+//        delete backend;
+//        backend = 0;
+//    }
+
+    cookedHeaders.clear();
+    rawHeaders.clear();
+
+    preMigrationDownloaded = bytesDownloaded;
+
+//    backend = manager->d_func()->findBackend(operation, request);
+
+//    if (backend) {
+//        backend->setParent(q);
+//        backend->reply = this;
+//        backend->setResumeOffset(bytesDownloaded);
+//    }
+
+    // FIXME
+    Q_ASSERT(0);
+    // What probably needs to be done is an abort and then re-send?
+
+    QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
+
+    return true;
+}
+
+
+void QNetworkReplyHttpImplPrivate::createCache()
+{
+    // check if we can save and if we're allowed to
+    if (!managerPrivate->networkCache
+        || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()
+        || request.attribute(QNetworkRequest::CacheLoadControlAttribute,
+                             QNetworkRequest::PreferNetwork).toInt()
+            == QNetworkRequest::AlwaysNetwork)
+        return;
+    cacheEnabled = true;
+}
+
+bool QNetworkReplyHttpImplPrivate::isCachingEnabled() const
+{
+    return (cacheEnabled && managerPrivate->networkCache != 0);
+}
+
+void QNetworkReplyHttpImplPrivate::setCachingEnabled(bool enable)
+{
+    if (!enable && !cacheEnabled)
+        return;                 // nothing to do
+    if (enable && cacheEnabled)
+        return;                 // nothing to do either!
+
+    if (enable) {
+        if (bytesDownloaded) {
+            qDebug() << "x" << bytesDownloaded;
+            // refuse to enable in this case
+            qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
+            return;
+        }
+
+        createCache();
+    } else {
+        // someone told us to turn on, then back off?
+        // ok... but you should make up your mind
+        qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false)");
+        managerPrivate->networkCache->remove(url);
+        cacheSaveDevice = 0;
+        cacheEnabled = false;
+    }
+}
+
+void QNetworkReplyHttpImplPrivate::completeCacheSave()
+{
+    if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
+        managerPrivate->networkCache->remove(url);
+    } else if (cacheEnabled && cacheSaveDevice) {
+        managerPrivate->networkCache->insert(cacheSaveDevice);
+    }
+    cacheSaveDevice = 0;
+    cacheEnabled = false;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
new file mode 100644 (file)
index 0000000..0420ffd
--- /dev/null
@@ -0,0 +1,309 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNETWORKREPLYHTTPIMPL_P_H
+#define QNETWORKREPLYHTTPIMPL_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists for the convenience
+// of the Network Access API.  This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qnetworkrequest.h"
+#include "qnetworkreply.h"
+
+#include "QtCore/qpointer.h"
+#include "QtCore/qdatetime.h"
+#include "QtCore/qsharedpointer.h"
+#include "qatomic.h"
+
+#include <QtNetwork/QNetworkCacheMetaData>
+#include <private/qhttpnetworkrequest_p.h>
+#include <private/qbytedata_p.h>
+#include <private/qnetworkreply_p.h>
+#include <QtNetwork/QNetworkProxy>
+
+#ifndef QT_NO_OPENSSL
+#include <QtNetwork/QSslConfiguration>
+#endif
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+class QNetworkReplyHttpImplPrivate;
+class QNetworkReplyHttpImpl: public QNetworkReply
+{
+    Q_OBJECT
+public:
+    QNetworkReplyHttpImpl(QNetworkAccessManager* const, const QNetworkRequest&, QNetworkAccessManager::Operation&, QIODevice* outgoingData);
+    virtual ~QNetworkReplyHttpImpl();
+
+    void close();
+    void abort();
+    qint64 bytesAvailable() const;
+    bool isSequential () const;
+    qint64 size() const;
+    qint64 readData(char*, qint64);
+    void setReadBufferSize(qint64 size);
+    bool canReadLine () const;
+
+#ifndef QT_NO_OPENSSL
+    void ignoreSslErrors();
+    // ### Qt5 Add proper virtual
+    Q_INVOKABLE void ignoreSslErrorsImplementation(const QList<QSslError> &errors);
+    // ### Qt5 Add proper virtual
+    Q_INVOKABLE void setSslConfigurationImplementation(const QSslConfiguration &configuration);
+    // ### Qt5 Add proper virtual
+    Q_INVOKABLE QSslConfiguration sslConfigurationImplementation() const;
+#endif
+
+    Q_DECLARE_PRIVATE(QNetworkReplyHttpImpl)
+    Q_PRIVATE_SLOT(d_func(), void _q_startOperation())
+    Q_PRIVATE_SLOT(d_func(), void _q_cacheLoadReadyRead())
+    Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingData())
+    Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingDataFinished())
+#ifndef QT_NO_BEARERMANAGEMENT
+    Q_PRIVATE_SLOT(d_func(), void _q_networkSessionConnected())
+    Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed())
+#endif
+    Q_PRIVATE_SLOT(d_func(), void _q_finished())
+    Q_PRIVATE_SLOT(d_func(), void _q_error(QNetworkReply::NetworkError, const QString &))
+
+    // From reply
+    Q_PRIVATE_SLOT(d_func(), void replyDownloadData(QByteArray))
+    Q_PRIVATE_SLOT(d_func(), void replyFinished())
+    Q_PRIVATE_SLOT(d_func(), void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64))
+    Q_PRIVATE_SLOT(d_func(), void replyDownloadProgressSlot(qint64,qint64))
+    Q_PRIVATE_SLOT(d_func(), void httpAuthenticationRequired(const QHttpNetworkRequest &, QAuthenticator *))
+    Q_PRIVATE_SLOT(d_func(), void httpError(QNetworkReply::NetworkError, const QString &))
+#ifndef QT_NO_OPENSSL
+    Q_PRIVATE_SLOT(d_func(), void replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *))
+    Q_PRIVATE_SLOT(d_func(), void replySslConfigurationChanged(const QSslConfiguration&))
+#endif
+    Q_PRIVATE_SLOT(d_func(), void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth))
+
+    Q_PRIVATE_SLOT(d_func(), void resetUploadDataSlot(bool *r))
+    Q_PRIVATE_SLOT(d_func(), void wantUploadDataSlot(qint64))
+    Q_PRIVATE_SLOT(d_func(), void sentUploadDataSlot(qint64))
+    Q_PRIVATE_SLOT(d_func(), void emitReplyUploadProgress(qint64, qint64))
+
+
+
+signals:
+    // To HTTP thread:
+    void startHttpRequest();
+    void abortHttpRequest();
+
+    void startHttpRequestSynchronously();
+
+    void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize);
+};
+
+class QNetworkReplyHttpImplPrivate: public QNetworkReplyPrivate
+{
+public:
+
+    static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& prio);
+
+    enum State {
+        Idle,               // The reply is idle.
+        Buffering,          // The reply is buffering outgoing data.
+        Working,            // The reply is uploading/downloading data.
+        Finished,           // The reply has finished.
+        Aborted,            // The reply has been aborted.
+        WaitingForSession,  // The reply is waiting for the session to open before connecting.
+        Reconnecting        // The reply will reconnect to once roaming has completed.
+    };
+
+    QNetworkReplyHttpImplPrivate();
+    ~QNetworkReplyHttpImplPrivate();
+
+    bool start();
+    void _q_startOperation();
+
+    void _q_cacheLoadReadyRead();
+
+    void _q_bufferOutgoingData();
+    void _q_bufferOutgoingDataFinished();
+
+#ifndef QT_NO_BEARERMANAGEMENT
+    void _q_networkSessionConnected();
+    void _q_networkSessionFailed();
+#endif
+    void _q_finished();
+
+
+    // FIXME
+    void finished();
+    void error(QNetworkReply::NetworkError code, const QString &errorString);
+    void _q_error(QNetworkReply::NetworkError code, const QString &errorString);
+    void metaDataChanged();
+
+    void redirectionRequested(const QUrl &target);
+    void checkForRedirect(const int statusCode);
+
+
+    // incoming from user
+    QNetworkAccessManager *manager;
+    QNetworkAccessManagerPrivate *managerPrivate;
+    QNetworkRequest request;
+    QNetworkAccessManager::Operation operation; // FIXME already in replyprivate?
+    QHttpNetworkRequest httpRequest; // There is also a copy in the HTTP thread
+    bool synchronous;
+
+    State state;
+
+    // from http thread
+    int statusCode;
+    QString reasonPhrase;
+
+    // upload
+    QNonContiguousByteDevice* createUploadByteDevice();
+    QSharedPointer<QNonContiguousByteDevice> uploadByteDevice;
+    QIODevice *outgoingData;
+    QSharedPointer<QRingBuffer> outgoingDataBuffer;
+    void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal); // dup?
+    qint64 bytesUploaded;
+
+
+    // cache
+    void createCache();
+    void completeCacheSave();
+    void setCachingEnabled(bool enable);
+    bool isCachingEnabled() const;
+    void initCacheSaveDevice();
+    QAbstractNetworkCache *networkCache() const;
+    QIODevice *cacheLoadDevice;
+    bool loadingFromCache;
+
+    QIODevice *cacheSaveDevice;
+    bool cacheEnabled; // is this for saving?
+
+
+    QUrl urlForLastAuthentication;
+#ifndef QT_NO_NETWORKPROXY
+    QNetworkProxy lastProxyAuthentication;
+    QList<QNetworkProxy> proxyList;
+#endif
+
+
+    bool migrateBackend();
+    bool canResume() const;
+    void setResumeOffset(quint64 offset);
+    quint64 resumeOffset;
+    qint64 preMigrationDownloaded;
+
+    // Used for normal downloading. For "zero copy" the downloadZerocopyBuffer is used
+    QByteDataBuffer downloadMultiBuffer;
+    QByteDataBuffer pendingDownloadData; // For signal compression
+    qint64 bytesDownloaded;
+    qint64 lastBytesDownloaded;
+    void setDownloadBuffer(QSharedPointer<char> sp, qint64 size);
+    char* getDownloadBuffer(qint64 size);
+
+    // only used when the "zero copy" style is used. Else downloadMultiBuffer is used.
+    // Please note that the whole "zero copy" download buffer API is private right now. Do not use it.
+    qint64 downloadBufferReadPosition;
+    qint64 downloadBufferCurrentSize;
+    qint64 downloadBufferMaximumSize;
+    QSharedPointer<char> downloadBufferPointer;
+    char* downloadZerocopyBuffer;
+
+    // Will be increased by HTTP thread:
+    QSharedPointer<QAtomicInt> pendingDownloadDataEmissions;
+    QSharedPointer<QAtomicInt> pendingDownloadProgressEmissions;
+
+
+#ifndef QT_NO_OPENSSL
+    QSslConfiguration sslConfiguration;
+    bool pendingIgnoreAllSslErrors;
+    QList<QSslError> pendingIgnoreSslErrorsList;
+#endif
+
+
+    bool loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest);
+    void invalidateCache();
+    bool sendCacheContents(const QNetworkCacheMetaData &metaData);
+    QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const;
+
+
+    void postRequest();
+
+
+
+public:
+    // From HTTP thread:
+    void replyDownloadData(QByteArray);
+    void replyFinished();
+    void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64);
+    void replyDownloadProgressSlot(qint64,qint64);
+    void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
+    void httpError(QNetworkReply::NetworkError error, const QString &errorString);
+#ifndef QT_NO_OPENSSL
+    void replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
+    void replySslConfigurationChanged(const QSslConfiguration&);
+#endif
+#ifndef QT_NO_NETWORKPROXY
+    void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth);
+#endif
+
+    // From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread:
+    void resetUploadDataSlot(bool *r);
+    void wantUploadDataSlot(qint64);
+    void sentUploadDataSlot(qint64);
+
+    Q_DECLARE_PUBLIC(QNetworkReplyHttpImpl)
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
+
+#endif
index 9eb505d..78e2463 100644 (file)
@@ -47,7 +47,6 @@
 #include "QtCore/qdatetime.h"
 #include "QtNetwork/qsslconfiguration.h"
 #include "QtNetwork/qnetworksession.h"
-#include "qnetworkaccesshttpbackend_p.h"
 #include "qnetworkaccessmanager_p.h"
 
 #include <QtCore/QCoreApplication>
@@ -356,7 +355,7 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const
         // for HTTP, we want to send out the request as fast as possible to the network, without
         // invoking methods in a QueuedConnection
 #ifndef QT_NO_HTTP
-        if (qobject_cast<QNetworkAccessHttpBackend *>(backend) || (backend && backend->isSynchronous())) {
+        if (backend && backend->isSynchronous()) {
             _q_startOperation();
         } else {
             QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
@@ -1043,11 +1042,7 @@ bool QNetworkReplyImplPrivate::migrateBackend()
     }
 
 #ifndef QT_NO_HTTP
-    if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) {
-        _q_startOperation();
-    } else {
-        QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
-    }
+    QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
 #else
     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
 #endif // QT_NO_HTTP
index 338969a..a9a1e49 100644 (file)
@@ -247,10 +247,13 @@ QT_BEGIN_NAMESPACE
 
     \value AlwaysNetwork        always load from network and do not
     check if the cache has a valid entry (similar to the
-    "Reload" feature in browsers)
+    "Reload" feature in browsers); in addition, force intermediate
+    caches to re-validate.
 
     \value PreferNetwork        default value; load from the network
-    if the cached entry is older than the network entry
+    if the cached entry is older than the network entry. This will never
+    return stale data from the cache, but revalidate resources that
+    have become stale.
 
     \value PreferCache          load from cache if available,
     otherwise load from network. Note that this can return possibly
index 70d7dd8..ab4d7f1 100644 (file)
@@ -233,7 +233,7 @@ void QSslConfiguration::setProtocol(QSsl::SslProtocol protocol)
     client), and whether it should require that this certificate is valid.
 
     The default mode is AutoVerifyPeer, which tells QSslSocket to use
-    VerifyPeer for clients, QueryPeer for clients.
+    VerifyPeer for clients, QueryPeer for servers.
 
     \sa setPeerVerifyMode()
 */
@@ -249,7 +249,7 @@ QSslSocket::PeerVerifyMode QSslConfiguration::peerVerifyMode() const
     client), and whether it should require that this certificate is valid.
 
     The default mode is AutoVerifyPeer, which tells QSslSocket to use
-    VerifyPeer for clients, QueryPeer for clients.
+    VerifyPeer for clients, QueryPeer for servers.
 
     \sa peerVerifyMode()
 */
index 76e6711..4777f4e 100644 (file)
@@ -137,6 +137,7 @@ static bool AlwaysFalse = false;
 
 Q_DECLARE_METATYPE(QNetworkRequest::CacheLoadControl)
 
+
 void tst_QAbstractNetworkCache::initTestCase()
 {
 #ifndef QT_NO_BEARERMANAGEMENT
@@ -150,6 +151,7 @@ void tst_QAbstractNetworkCache::initTestCase()
 #endif
 }
 
+
 void tst_QAbstractNetworkCache::expires_data()
 {
     QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
@@ -261,14 +263,14 @@ void tst_QAbstractNetworkCache::cacheControl_data()
 
     QTest::newRow("200-2") << QNetworkRequest::AlwaysNetwork << "httpcachetest_cachecontrol.cgi?no-cache" << AlwaysFalse;
     QTest::newRow("200-3") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?no-cache" << false;
-    QTest::newRow("200-4") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?no-cache" << false;//AlwaysTrue;
+    QTest::newRow("200-4") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?no-cache" << false;
     QTest::newRow("200-5") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol.cgi?no-cache" << false;
 
     QTest::newRow("304-0") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000" << true;
 
     QTest::newRow("304-1") << QNetworkRequest::AlwaysNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << AlwaysFalse;
     QTest::newRow("304-2") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << true;
-    QTest::newRow("304-3") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << AlwaysTrue;
+    QTest::newRow("304-3") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << false;
     QTest::newRow("304-4") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << true;
 
     // see QTBUG-7060
index e0c477b..9a58482 100644 (file)
@@ -182,6 +182,14 @@ void tst_QNetworkCookie::parseSingleCookie_data()
     cookie.setValue("\"\\\"a, b; c\\\"\"");
     QTest::newRow("with-value-with-special5") << "a = \"\\\"a, b; c\\\"\"" << cookie;
 
+    cookie.setValue("b c");
+    QTest::newRow("with-value-with-whitespace") << "a = b c" << cookie;
+
+    cookie.setValue("\"b\"");
+    QTest::newRow("quoted-value") << "a = \"b\"" << cookie;
+    cookie.setValue("\"b c\"");
+    QTest::newRow("quoted-value-with-whitespace") << "a = \"b c\"" << cookie;
+
     cookie.setValue("b");
     cookie.setSecure(true);
     QTest::newRow("secure") << "a=b;secure" << cookie;
index f509cea..f7365df 100644 (file)
@@ -3237,16 +3237,16 @@ void tst_QNetworkReply::ioGetFromHttpWithCache_data()
     QTest::newRow("must-revalidate,200,prefer-network")
             << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << false << true;
     QTest::newRow("must-revalidate,200,prefer-cache")
-            << reply200 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << false;
+            << reply200 << "Reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << false << true;
     QTest::newRow("must-revalidate,200,always-cache")
-            << reply200 << "Not-reloaded" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << true << false;
+            << reply200 << "" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false;
 
     QTest::newRow("must-revalidate,304,prefer-network")
             << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferNetwork) << QStringList() << true << true;
     QTest::newRow("must-revalidate,304,prefer-cache")
-            << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << false;
+            << reply304 << "Not-reloaded" << content << int(QNetworkRequest::PreferCache) << QStringList() << true << true;
     QTest::newRow("must-revalidate,304,always-cache")
-            << reply304 << "Not-reloaded" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << true << false;
+            << reply304 << "" << content << int(QNetworkRequest::AlwaysCache) << QStringList() << false << false;
 
     //
     // Partial content
@@ -4162,6 +4162,7 @@ public:
 
         if (serverSocket->setSocketDescriptor(socketDescriptor)) {
             connect(serverSocket, SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
+            connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
             serverSocket->setProtocol(QSsl::AnyProtocol);
             connect(serverSocket, SIGNAL(sslErrors(const QList<QSslError>&)), serverSocket, SLOT(ignoreSslErrors()));
             serverSocket->setLocalCertificate(SRCDIR "/certs/server.pem");
@@ -4178,6 +4179,11 @@ public slots:
         socket = (QSslSocket*) sender();
         emit newEncryptedConnection();
     }
+    void readyReadSlot() {
+        // for the incoming sockets, not the server socket
+        //qDebug() << static_cast<QSslSocket*>(sender())->bytesAvailable() << static_cast<QSslSocket*>(sender())->encryptedBytesAvailable();
+    }
+
 public:
     QSslSocket *socket;
 };
@@ -4185,8 +4191,15 @@ public:
 // very similar to ioPostToHttpUploadProgress but for SSL
 void tst_QNetworkReply::ioPostToHttpsUploadProgress()
 {
-    QFile sourceFile(SRCDIR "/bigfile");
-    QVERIFY(sourceFile.open(QIODevice::ReadOnly));
+    //QFile sourceFile(SRCDIR "/bigfile");
+    //QVERIFY(sourceFile.open(QIODevice::ReadOnly));
+    qint64 wantedSize = 2*1024*1024; // 2 MB
+    QByteArray sourceFile;
+    // And in the case of SSL, the compression can fool us and let the
+    // server send the data much faster than expected.
+    // So better provide random data that cannot be compressed.
+    for (int i = 0; i < wantedSize; ++i)
+        sourceFile += (char)qrand();
 
     // emulate a minimal https server
     SslServer server;
@@ -4195,8 +4208,10 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress()
     // create the request
     QUrl url = QUrl(QString("https://127.0.0.1:%1/").arg(server.serverPort()));
     QNetworkRequest request(url);
+
     request.setRawHeader("Content-Type", "application/octet-stream");
-    QNetworkReplyPtr reply = manager.post(request, &sourceFile);
+    QNetworkReplyPtr reply = manager.post(request, sourceFile);
+
     QSignalSpy spy(reply, SIGNAL(uploadProgress(qint64,qint64)));
     connect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
     connect(reply, SIGNAL(sslErrors(const QList<QSslError>&)), reply, SLOT(ignoreSslErrors()));
@@ -4215,26 +4230,17 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress()
     QVERIFY(!spy.isEmpty());
     QList<QVariant> args = spy.last();
     QVERIFY(args.at(0).toLongLong() > 0);
-
+    // but not everything!
     QVERIFY(args.at(0).toLongLong() != sourceFile.size());
 
-    incomingSocket->setReadBufferSize(32*1024);
-    incomingSocket->read(16*1024);
-    QTestEventLoop::instance().enterLoop(2);
-    // some more progress than before
-    QVERIFY(!spy.isEmpty());
-    QList<QVariant> args2 = spy.last();
-    QVERIFY(args2.at(0).toLongLong() > args.at(0).toLongLong());
-
     // set the read buffer to unlimited
     incomingSocket->setReadBufferSize(0);
     QTestEventLoop::instance().enterLoop(10);
     // progress should be finished
     QVERIFY(!spy.isEmpty());
     QList<QVariant> args3 = spy.last();
-    QVERIFY(args3.at(0).toLongLong() > args2.at(0).toLongLong());
     QCOMPARE(args3.at(0).toLongLong(), args3.at(1).toLongLong());
-    QCOMPARE(args3.at(0).toLongLong(), sourceFile.size());
+    QCOMPARE(args3.at(0).toLongLong(), qint64(sourceFile.size()));
 
     // after sending this, the QNAM should emit finished()
     connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));