QNAM: Start new HTTP backend architecture
authorMarkus Goetz <Markus.Goetz@nokia.com>
Mon, 28 Mar 2011 12:56:26 +0000 (14:56 +0200)
committerMarkus Goetz <Markus.Goetz@nokia.com>
Tue, 3 May 2011 14:34:54 +0000 (16:34 +0200)
src/network/access/access.pri
src/network/access/qnetworkaccessmanager.cpp
src/network/access/qnetworkaccessmanager.h
src/network/access/qnetworkreplyhttpimpl.cpp
src/network/access/qnetworkreplyhttpimpl_p.h
src/network/access/qnetworkreplyimpl.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 34ac170..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);
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 e75347f..d38850b 100644 (file)
@@ -41,7 +41,7 @@
 
 //#define QNETWORKACCESSHTTPBACKEND_DEBUG
 
-#include "qnetworkaccesshttpbackend_p.h"
+#include "qnetworkreplyhttpimpl_p.h"
 #include "qnetworkaccessmanager_p.h"
 #include "qnetworkaccesscache_p.h"
 #include "qabstractnetworkcache.h"
@@ -55,6 +55,8 @@
 #include "qhttpthreaddelegate_p.h"
 #include "qthread.h"
 
+#include "qnetworkcookiejar.h"
+
 #ifndef QT_NO_HTTP
 
 #include <string.h>             // for strchr
@@ -162,55 +164,231 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
     }
 }
 
-QNetworkAccessBackend *
-QNetworkAccessHttpBackendFactory::create(QNetworkAccessManager::Operation op,
-                                         const QNetworkRequest &request) const
+QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manager,
+                                             const QNetworkRequest& request,
+                                             QNetworkAccessManager::Operation& operation,
+                                             QIODevice* outgoingData)
+    : QNetworkReply(*new QNetworkReplyHttpImplPrivate, manager)
 {
-    // check the operation
-    switch (op) {
-    case QNetworkAccessManager::GetOperation:
-    case QNetworkAccessManager::PostOperation:
-    case QNetworkAccessManager::HeadOperation:
-    case QNetworkAccessManager::PutOperation:
-    case QNetworkAccessManager::DeleteOperation:
-    case QNetworkAccessManager::CustomOperation:
-        break;
+    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
 
-    default:
-        // no, we can't handle this request
-        return 0;
+    // 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;
+        }
     }
 
-    QUrl url = request.url();
-    QString scheme = url.scheme().toLower();
-    if (scheme == QLatin1String("http") || scheme == QLatin1String("https"))
-        return new QNetworkAccessHttpBackend;
 
-    return 0;
+    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()
+{
+}
+
+void QNetworkReplyHttpImpl::close()
+{
+    Q_D(QNetworkReplyHttpImpl);
+    QNetworkReply::close();
+    // FIXME
+}
+
+void QNetworkReplyHttpImpl::abort()
+{
+    Q_D(QNetworkReplyHttpImpl);
+    QNetworkReply::close();
+    // FIXME
+}
+
+qint64 QNetworkReplyHttpImpl::bytesAvailable() const
+{
+    Q_D(const QNetworkReplyHttpImpl);
+    qDebug() << "QNetworkReplyHttpImpl::bytesAvailable()";
+
+    // FIXME cache device
+    if (d->cacheLoadDevice) {
+        return QNetworkReply::bytesAvailable() + d->cacheLoadDevice->bytesAvailable() + d->downloadMultiBuffer.byteAmount();
+    }
+
+    // FIXME 0-copy buffer
+    if (d->downloadZerocopyBuffer) {
+        return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
+    }
+
+    // FIXME normal buffer
+    qDebug() << "QNetworkReplyHttpImpl::bytesAvailable() ==" << QNetworkReply::bytesAvailable() + d->downloadMultiBuffer.byteAmount();
+    return QNetworkReply::bytesAvailable() + d->downloadMultiBuffer.byteAmount();
+
+    // FIXME
 }
 
-QNetworkAccessHttpBackend::QNetworkAccessHttpBackend()
-    : QNetworkAccessBackend()
+bool QNetworkReplyHttpImpl::isSequential () const
+{
+    // FIXME Maybe not for cache or 0-copy buffer
+    return true;
+}
+
+qint64 QNetworkReplyHttpImpl::size() const
+{
+    Q_D(const QNetworkReplyHttpImpl);
+    //return -1;
+    return QNetworkReply::size();
+}
+
+qint64 QNetworkReplyHttpImpl::readData(char* data, qint64 maxlen)
+{
+    Q_D(QNetworkReplyHttpImpl);
+    qDebug() << "QNetworkReplyHttpImpl::readData()" << maxlen;
+
+    // FIXME 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;
+    }
+
+    // FIXME 0-copy buffer
+    if (d->downloadZerocopyBuffer) {
+        // bla
+        qint64 howMuch = qMin(maxlen, (d->downloadBufferCurrentSize - d->downloadBufferReadPosition));
+        memcpy(data, d->downloadZerocopyBuffer + d->downloadBufferReadPosition, howMuch);
+        d->downloadBufferReadPosition += howMuch;
+        return howMuch;
+
+    }
+
+    // FIXME normal buffer
+    if (d->downloadMultiBuffer.isEmpty())
+        return d->state == d->Finished ? -1 : 0;
+    // FIXME what about "Aborted" state?
+
+    //d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
+    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)
+{
+    return; // FIXME, unsupported right now
+}
+
+bool QNetworkReplyHttpImpl::canReadLine () const
+{
+    Q_D(const QNetworkReplyHttpImpl);
+
+    if (QNetworkReply::canReadLine())
+        return true;
+
+    if (d->cacheLoadDevice)
+        return d->cacheLoadDevice->canReadLine() || d->downloadMultiBuffer.canReadLine();
+
+    return d->downloadMultiBuffer.canReadLine();
+}
+
+QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate()
+// FIXME order etc
+    : QNetworkReplyPrivate()
     , statusCode(0)
     , pendingDownloadDataEmissions(new QAtomicInt())
     , pendingDownloadProgressEmissions(new QAtomicInt())
     , loadingFromCache(false)
-    , usingZerocopyDownloadBuffer(false)
 #ifndef QT_NO_OPENSSL
-    , pendingSslConfiguration(0), pendingIgnoreAllSslErrors(false)
+    , pendingIgnoreAllSslErrors(false)
 #endif
     , resumeOffset(0)
+    , outgoingData(0),
+      cacheLoadDevice(0),
+      cacheEnabled(false), cacheSaveDevice(0),
+     // notificationHandlingPaused(false),
+      bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
+      //httpStatusCode(0),
+      state(Idle)
+      , downloadBufferReadPosition(0)
+      , downloadBufferCurrentSize(0)
+      , downloadBufferMaximumSize(0)
+      , downloadZerocopyBuffer(0)
+    , synchronous(false)
+
 {
 }
 
-QNetworkAccessHttpBackend::~QNetworkAccessHttpBackend()
+QNetworkReplyHttpImplPrivate::~QNetworkReplyHttpImplPrivate()
 {
+    Q_Q(QNetworkReplyHttpImpl);
     // This will do nothing if the request was already finished or aborted
-    emit abortHttpRequest();
-
-#ifndef QT_NO_OPENSSL
-    delete pendingSslConfiguration;
-#endif
+    emit q->abortHttpRequest();
 }
 
 /*
@@ -219,14 +397,14 @@ QNetworkAccessHttpBackend::~QNetworkAccessHttpBackend()
     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
  */
-void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache)
+void QNetworkReplyHttpImplPrivate::validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache)
 {
     QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
-        (QNetworkRequest::CacheLoadControl)request().attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
+        (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")) {
+        if (!request.rawHeaderList().contains("Cache-Control")) {
             httpRequest.setHeaderField("Cache-Control", "no-cache");
             httpRequest.setHeaderField("Pragma", "no-cache");
         }
@@ -235,14 +413,14 @@ void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest,
 
     // 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"))
+    if (request.hasRawHeader("Range"))
         return;
 
-    QAbstractNetworkCache *nc = networkCache();
+    QAbstractNetworkCache *nc = managerPrivate->networkCache;
     if (!nc)
         return;                 // no local cache
 
-    QNetworkCacheMetaData metaData = nc->metaData(url());
+    QNetworkCacheMetaData metaData = nc->metaData(request.url());
     if (!metaData.isValid())
         return;                 // not in cache
 
@@ -348,7 +526,7 @@ void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest,
         loadedFromCache = false;
 }
 
-static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& prio)
+QHttpNetworkRequest::Priority QNetworkReplyHttpImplPrivate::convert(const QNetworkRequest::Priority& prio)
 {
     switch (prio) {
     case QNetworkRequest::LowPriority:
@@ -361,50 +539,44 @@ static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& pr
     }
 }
 
-void QNetworkAccessHttpBackend::postRequest()
+void QNetworkReplyHttpImplPrivate::postRequest()
 {
+    Q_Q(QNetworkReplyHttpImpl);
+
     QThread *thread = 0;
-    if (isSynchronous()) {
+    if (synchronous) {
         // A synchronous HTTP request uses its own thread
+        qDebug() << "sync!";
         thread = new QThread();
         QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
         thread->start();
-    } else if (!manager->httpThread) {
+    } else if (!managerPrivate->httpThread) {
         // We use the manager-global thread.
         // At some point we could switch to having multiple threads if it makes sense.
-        manager->httpThread = new QThread();
-        QObject::connect(manager->httpThread, SIGNAL(finished()), manager->httpThread, SLOT(deleteLater()));
-        manager->httpThread->start();
-#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>");
+        managerPrivate->httpThread = new QThread();
+        QObject::connect(managerPrivate->httpThread, SIGNAL(finished()), managerPrivate->httpThread, SLOT(deleteLater()));
+        managerPrivate->httpThread->start();
 
-        thread = manager->httpThread;
+        thread = managerPrivate->httpThread;
     } else {
         // Asynchronous request, thread already exists
-        thread = manager->httpThread;
+        thread = managerPrivate->httpThread;
     }
 
-    QUrl url = request().url();
+    QUrl url = request.url();
     httpRequest.setUrl(url);
 
     bool ssl = url.scheme().toLower() == QLatin1String("https");
-    setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl);
+    q->setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl);
     httpRequest.setSsl(ssl);
 
 
 #ifndef QT_NO_NETWORKPROXY
     QNetworkProxy transparentProxy, cacheProxy;
 
-    foreach (const QNetworkProxy &p, proxyList()) {
+    // 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
@@ -427,19 +599,19 @@ void QNetworkAccessHttpBackend::postRequest()
     if (transparentProxy.type() == QNetworkProxy::DefaultProxy &&
         cacheProxy.type() == QNetworkProxy::DefaultProxy) {
         // unsuitable proxies
-        QMetaObject::invokeMethod(this, "error", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection,
+        QMetaObject::invokeMethod(q, "error", synchronous ? Qt::DirectConnection : Qt::QueuedConnection,
                                   Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError),
-                                  Q_ARG(QString, tr("No suitable proxy found")));
-        QMetaObject::invokeMethod(this, "finished", isSynchronous() ? Qt::DirectConnection : Qt::QueuedConnection);
+                                  Q_ARG(QString, q->tr("No suitable proxy found")));
+        QMetaObject::invokeMethod(q, "finished", synchronous ? Qt::DirectConnection : Qt::QueuedConnection);
         return;
     }
 #endif
 
 
     bool loadedFromCache = false;
-    httpRequest.setPriority(convert(request().priority()));
+    httpRequest.setPriority(convert(request.priority()));
 
-    switch (operation()) {
+    switch (operation) {
     case QNetworkAccessManager::GetOperation:
         httpRequest.setOperation(QHttpNetworkRequest::Get);
         validateCache(httpRequest, loadedFromCache);
@@ -471,7 +643,7 @@ void QNetworkAccessHttpBackend::postRequest()
         invalidateCache(); // for safety reasons, we don't know what the operation does
         httpRequest.setOperation(QHttpNetworkRequest::Custom);
         createUploadByteDevice();
-        httpRequest.setCustomVerb(request().attribute(
+        httpRequest.setCustomVerb(request.attribute(
                 QNetworkRequest::CustomVerbAttribute).toByteArray());
         break;
 
@@ -479,7 +651,7 @@ void QNetworkAccessHttpBackend::postRequest()
         break;                  // can't happen
     }
 
-    QList<QByteArray> headers = request().rawHeaderList();
+    QList<QByteArray> headers = request.rawHeaderList();
     if (resumeOffset != 0) {
         if (headers.contains("Range")) {
             // Need to adjust resume offset for user specified range
@@ -487,7 +659,7 @@ void QNetworkAccessHttpBackend::postRequest()
             headers.removeOne("Range");
 
             // We've already verified that requestRange starts with "bytes=", see canResume.
-            QByteArray requestRange = request().rawHeader("Range").mid(6);
+            QByteArray requestRange = request.rawHeader("Range").mid(6);
 
             int index = requestRange.indexOf('-');
 
@@ -504,20 +676,17 @@ void QNetworkAccessHttpBackend::postRequest()
     }
 
     foreach (const QByteArray &header, headers)
-        httpRequest.setHeaderField(header, request().rawHeader(header));
+        httpRequest.setHeaderField(header, request.rawHeader(header));
 
     if (loadedFromCache) {
-        // commented this out since it will be called later anyway
-        // by copyFinished()
-        //QNetworkAccessBackend::finished();
         return;    // no need to send the request! :)
     }
 
-    if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)
+    if (request.attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)
         httpRequest.setPipeliningAllowed(true);
 
     if (static_cast<QNetworkRequest::LoadControl>
-        (request().attribute(QNetworkRequest::AuthenticationReuseAttribute,
+        (request.attribute(QNetworkRequest::AuthenticationReuseAttribute,
                              QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
         httpRequest.setWithCredentials(false);
 
@@ -527,7 +696,7 @@ void QNetworkAccessHttpBackend::postRequest()
 
     // 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
-    connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
+    QObject::connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
 
     // Set the properties it needs
     delegate->httpRequest = httpRequest;
@@ -538,63 +707,63 @@ void QNetworkAccessHttpBackend::postRequest()
     delegate->ssl = ssl;
 #ifndef QT_NO_OPENSSL
     if (ssl)
-        delegate->incomingSslConfiguration = request().sslConfiguration();
+        delegate->incomingSslConfiguration = request.sslConfiguration();
 #endif
 
     // Do we use synchronous HTTP?
-    delegate->synchronous = isSynchronous();
+    delegate->synchronous = synchronous;
 
     // The authentication manager is used to avoid the BlockingQueuedConnection communication
     // from HTTP thread to user thread in some cases.
-    delegate->authenticationManager = manager->authenticationManager;
+    delegate->authenticationManager = managerPrivate->authenticationManager;
 
-    if (!isSynchronous()) {
+    if (!synchronous) {
         // Tell our zerocopy policy to the delegate
         delegate->downloadBufferMaximumSize =
-                request().attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong();
+                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
-        connect(delegate, SIGNAL(downloadData(QByteArray)),
-                this, SLOT(replyDownloadData(QByteArray)),
+        QObject::connect(delegate, SIGNAL(downloadData(QByteArray)),
+                q, SLOT(replyDownloadData(QByteArray)),
                 Qt::QueuedConnection);
-        connect(delegate, SIGNAL(downloadFinished()),
-                this, SLOT(replyFinished()),
+        QObject::connect(delegate, SIGNAL(downloadFinished()),
+                q, SLOT(replyFinished()),
                 Qt::QueuedConnection);
-        connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
-                this, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
+        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);
-        connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
-                this, SLOT(replyDownloadProgressSlot(qint64,qint64)),
+        QObject::connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
+                q, SLOT(replyDownloadProgressSlot(qint64,qint64)),
                 Qt::QueuedConnection);
-        connect(delegate, SIGNAL(error(QNetworkReply::NetworkError,QString)),
-                this, SLOT(httpError(QNetworkReply::NetworkError, const QString)),
+        QObject::connect(delegate, SIGNAL(error(QNetworkReply::NetworkError,QString)),
+                q, SLOT(httpError(QNetworkReply::NetworkError, const QString)),
                 Qt::QueuedConnection);
 #ifndef QT_NO_OPENSSL
-        connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
-                this, SLOT(replySslConfigurationChanged(QSslConfiguration)),
+        QObject::connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
+                q, SLOT(replySslConfigurationChanged(QSslConfiguration)),
                 Qt::QueuedConnection);
 #endif
         // Those need to report back, therefire BlockingQueuedConnection
-        connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
-                this, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+        QObject::connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
+                q, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
                 Qt::BlockingQueuedConnection);
 #ifndef QT_NO_NETWORKPROXY
-        connect (delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
-                 this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+        QObject::connect(delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
+                 q, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
                  Qt::BlockingQueuedConnection);
 #endif
 #ifndef QT_NO_OPENSSL
-        connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
-                this, SLOT(replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *)),
+        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.
-        connect(this, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));
-        connect(this, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));
+        QObject::connect(q, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));
+        QObject::connect(q, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));
 
         if (uploadByteDevice) {
             QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice =
@@ -605,7 +774,7 @@ void QNetworkAccessHttpBackend::postRequest()
             delegate->httpRequest.setUploadByteDevice(forwardUploadDevice);
 
             // From main thread to user thread:
-            QObject::connect(this, SIGNAL(haveUploadData(QByteArray, bool, qint64)),
+            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()),
@@ -613,22 +782,22 @@ void QNetworkAccessHttpBackend::postRequest()
 
             // From http thread to user thread:
             QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),
-                             this, SLOT(wantUploadDataSlot(qint64)));
+                             q, SLOT(wantUploadDataSlot(qint64)));
             QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)),
-                             this, SLOT(sentUploadDataSlot(qint64)));
-            connect(forwardUploadDevice, SIGNAL(resetData(bool*)),
-                    this, SLOT(resetUploadDataSlot(bool*)),
+                             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 (isSynchronous()) {
-        connect(this, SIGNAL(startHttpRequestSynchronously()), delegate, SLOT(startRequestSynchronously()), Qt::BlockingQueuedConnection);
+    } 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 QNetworkReplyImplPrivate::setup() makes sure it is safe to use from a thread
+            // 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());
         }
@@ -640,8 +809,8 @@ void QNetworkAccessHttpBackend::postRequest()
     // 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 (isSynchronous()) {
-        emit startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
+    if (synchronous) {
+        emit q->startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
 
         if (delegate->incomingErrorCode != QNetworkReply::NoError) {
             replyDownloadMetaData
@@ -668,41 +837,61 @@ void QNetworkAccessHttpBackend::postRequest()
 
         finished();
     } else {
-        emit startHttpRequest(); // Signal to the HTTP thread and go back to user.
+        emit q->startHttpRequest(); // Signal to the HTTP thread and go back to user.
     }
 }
 
-void QNetworkAccessHttpBackend::invalidateCache()
+void QNetworkReplyHttpImplPrivate::invalidateCache()
 {
-    QAbstractNetworkCache *nc = networkCache();
+    QAbstractNetworkCache *nc = managerPrivate->networkCache;
     if (nc)
-        nc->remove(url());
+        nc->remove(request.url());
 }
 
-void QNetworkAccessHttpBackend::open()
+void QNetworkReplyHttpImplPrivate::initCacheSaveDevice()
 {
-    postRequest();
-}
+    Q_Q(QNetworkReplyHttpImpl);
 
-void QNetworkAccessHttpBackend::closeDownstreamChannel()
-{
-    // FIXME Maybe we can get rid of this whole architecture part
-}
+    // 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;
+    }
 
-void QNetworkAccessHttpBackend::downstreamReadyWrite()
-{
-    // FIXME Maybe we can get rid of this whole architecture part
-}
+    // 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);
+    }
 
-void QNetworkAccessHttpBackend::setDownstreamLimited(bool b)
-{
-    Q_UNUSED(b);
-    // We know that readBuffer maximum size limiting is broken since quite a while.
-    // The task to fix this is QTBUG-15065
+    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 QNetworkAccessHttpBackend::replyDownloadData(QByteArray d)
+void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
 {
+    Q_Q(QNetworkReplyHttpImpl);
+
+    qDebug() << "QNetworkReplyHttpImplPrivate::replyDownloadData" << d.size();
+
     int pendingSignals = (int)pendingDownloadDataEmissions->fetchAndAddAcquire(-1) - 1;
 
     if (pendingSignals > 0) {
@@ -722,10 +911,54 @@ void QNetworkAccessHttpBackend::replyDownloadData(QByteArray d)
     // processEvents() or spin an event loop when this occur.
     QByteDataBuffer pendingDownloadDataCopy = pendingDownloadData;
     pendingDownloadData.clear();
-    writeDownstreamData(pendingDownloadDataCopy);
+
+    // FIXME
+    //writeDownstreamData(pendingDownloadDataCopy);
+    // instead we do:
+
+    // We could be closed
+    if (!q->isOpen())
+        return;
+
+    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;
+
+    //appendDownstreamDataSignalEmissions();
+    // instead:
+    QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+    if (preMigrationDownloaded != Q_INT64_C(-1))
+        totalSize = totalSize.toLongLong() + preMigrationDownloaded;
+//    pauseNotificationHandling();
+    // important: At the point of this readyRead(), the data parameter list must be empty,
+    // else implicit sharing will trigger memcpy when the user is reading data!
+    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());
+
+//    resumeNotificationHandling();
+
+
 }
 
-void QNetworkAccessHttpBackend::replyFinished()
+void QNetworkReplyHttpImplPrivate::replyFinished()
 {
     // We are already loading from cache, we still however
     // got this signal because it was posted already
@@ -735,8 +968,9 @@ void QNetworkAccessHttpBackend::replyFinished()
     finished();
 }
 
-void QNetworkAccessHttpBackend::checkForRedirect(const int statusCode)
+void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
 {
+    Q_Q(QNetworkReplyHttpImpl);
     switch (statusCode) {
     case 301:                   // Moved Permanently
     case 302:                   // Found
@@ -745,32 +979,38 @@ void QNetworkAccessHttpBackend::checkForRedirect(const int statusCode)
         // 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 = rawHeader("location");
+        QByteArray header = q->rawHeader("location");
         QUrl url = QUrl::fromEncoded(header);
         if (!url.isValid())
             url = QUrl(QLatin1String(header));
-        redirectionRequested(url);
+        //redirectionRequested(url);
+        q->setAttribute(QNetworkRequest::RedirectionTargetAttribute, url);
     }
 }
 
-void QNetworkAccessHttpBackend::replyDownloadMetaData
+void QNetworkReplyHttpImplPrivate::replyDownloadMetaData
         (QList<QPair<QByteArray,QByteArray> > hm,
          int sc,QString rp,bool pu,
          QSharedPointer<char> db,
          qint64 contentLength)
 {
+    qDebug() << "QNetworkReplyHttpImplPrivate::replyDownloadMetaData" << contentLength << sc;
+    Q_Q(QNetworkReplyHttpImpl);
+
     statusCode = sc;
     reasonPhrase = rp;
 
     // Download buffer
     if (!db.isNull()) {
-        reply->setDownloadBuffer(db, contentLength);
-        usingZerocopyDownloadBuffer = true;
-    } else {
-        usingZerocopyDownloadBuffer = false;
+        //setDownloadBuffer(db, contentLength);
+        downloadBufferPointer = db;
+        downloadZerocopyBuffer = downloadBufferPointer.data();
+        downloadBufferCurrentSize = 0;
+        downloadBufferMaximumSize = contentLength;
+        q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
     }
 
-    setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu);
+    q->setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu);
 
     // reconstruct the HTTP header
     QList<QPair<QByteArray, QByteArray> > headerMap = hm;
@@ -779,7 +1019,7 @@ void QNetworkAccessHttpBackend::replyDownloadMetaData
     QByteArray header;
 
     for (; it != end; ++it) {
-        QByteArray value = rawHeader(it->first);
+        QByteArray value = q->rawHeader(it->first);
         if (!value.isEmpty()) {
             if (qstricmp(it->first.constData(), "set-cookie") == 0)
                 value += '\n';
@@ -787,19 +1027,19 @@ void QNetworkAccessHttpBackend::replyDownloadMetaData
                 value += ", ";
         }
         value += it->second;
-        setRawHeader(it->first, value);
+        q->setRawHeader(it->first, value);
     }
 
-    setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
-    setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
+    q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
+    q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
 
     // is it a redirection?
     checkForRedirect(statusCode);
 
     if (statusCode >= 500 && statusCode < 600) {
-        QAbstractNetworkCache *nc = networkCache();
+        QAbstractNetworkCache *nc = managerPrivate->networkCache;
         if (nc) {
-            QNetworkCacheMetaData metaData = nc->metaData(url());
+            QNetworkCacheMetaData metaData = nc->metaData(request.url());
             QNetworkHeadersPrivate cacheHeaders;
             cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
             QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
@@ -819,9 +1059,9 @@ void QNetworkAccessHttpBackend::replyDownloadMetaData
 #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
         qDebug() << "Received a 304 from" << url();
 #endif
-        QAbstractNetworkCache *nc = networkCache();
+        QAbstractNetworkCache *nc = managerPrivate->networkCache;
         if (nc) {
-            QNetworkCacheMetaData oldMetaData = nc->metaData(url());
+            QNetworkCacheMetaData oldMetaData = nc->metaData(request.url());
             QNetworkCacheMetaData metaData = fetchCacheMetaData(oldMetaData);
             if (oldMetaData != metaData)
                 nc->updateMetaData(metaData);
@@ -839,8 +1079,10 @@ void QNetworkAccessHttpBackend::replyDownloadMetaData
     metaDataChanged();
 }
 
-void QNetworkAccessHttpBackend::replyDownloadProgressSlot(qint64 received,  qint64 total)
+void QNetworkReplyHttpImplPrivate::replyDownloadProgressSlot(qint64 bytesReceived,  qint64 bytesTotal)
 {
+    Q_Q(QNetworkReplyHttpImpl);
+
     // we can be sure here that there is a download buffer
 
     int pendingSignals = (int)pendingDownloadProgressEmissions->fetchAndAddAcquire(-1) - 1;
@@ -850,32 +1092,63 @@ void QNetworkAccessHttpBackend::replyDownloadProgressSlot(qint64 received,  qint
         return;
     }
 
-    // Now do the actual notification of new bytes
-    writeDownstreamDataDownloadBuffer(received, total);
+    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 QNetworkAccessHttpBackend::httpAuthenticationRequired(const QHttpNetworkRequest &,
+void QNetworkReplyHttpImplPrivate::httpAuthenticationRequired(const QHttpNetworkRequest &,
                                                            QAuthenticator *auth)
 {
-    authenticationRequired(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 QNetworkAccessHttpBackend::httpError(QNetworkReply::NetworkError errorCode,
+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 QNetworkAccessHttpBackend::replySslErrors(
+void QNetworkReplyHttpImplPrivate::replySslErrors(
         const QList<QSslError> &list, bool *ignoreAll, QList<QSslError> *toBeIgnored)
 {
-    // Go to generic backend
-    sslErrors(list);
+    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;
@@ -883,31 +1156,30 @@ void QNetworkAccessHttpBackend::replySslErrors(
         *toBeIgnored = pendingIgnoreSslErrorsList;
 }
 
-void QNetworkAccessHttpBackend::replySslConfigurationChanged(const QSslConfiguration &c)
+void QNetworkReplyHttpImplPrivate::replySslConfigurationChanged(const QSslConfiguration &sslConfiguration)
 {
     // Receiving the used SSL configuration from the HTTP thread
-    if (pendingSslConfiguration)
-        *pendingSslConfiguration = c;
-    else if (!c.isNull())
-        pendingSslConfiguration = new QSslConfiguration(c);
+    this->sslConfiguration = sslConfiguration;
 }
 #endif
 
 // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
-void QNetworkAccessHttpBackend::resetUploadDataSlot(bool *r)
+void QNetworkReplyHttpImplPrivate::resetUploadDataSlot(bool *r)
 {
     *r = uploadByteDevice->reset();
 }
 
 // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
-void QNetworkAccessHttpBackend::sentUploadDataSlot(qint64 amount)
+void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 amount)
 {
     uploadByteDevice->advanceReadPointer(amount);
 }
 
 // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
-void QNetworkAccessHttpBackend::wantUploadDataSlot(qint64 maxSize)
+void QNetworkReplyHttpImplPrivate::wantUploadDataSlot(qint64 maxSize)
 {
+    Q_Q(QNetworkReplyHttpImpl);
+
     // call readPointer
     qint64 currentUploadDataLength = 0;
     char *data = const_cast<char*>(uploadByteDevice->readPointer(maxSize, currentUploadDataLength));
@@ -915,37 +1187,39 @@ void QNetworkAccessHttpBackend::wantUploadDataSlot(qint64 maxSize)
     QByteArray dataArray(data, currentUploadDataLength);
 
     // Communicate back to HTTP thread
-    emit haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size());
+    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 QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &metaData)
+bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData &metaData)
 {
+    Q_Q(QNetworkReplyHttpImpl);
+
     setCachingEnabled(false);
     if (!metaData.isValid())
         return false;
 
-    QAbstractNetworkCache *nc = networkCache();
+    QAbstractNetworkCache *nc = managerPrivate->networkCache;
     Q_ASSERT(nc);
-    QIODevice *contents = nc->data(url());
+    QIODevice *contents = nc->data(url);
     if (!contents) {
 #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
-        qDebug() << "Can not send cache, the contents are 0" << url();
+        qDebug() << "Can not send cache, the contents are 0" << url;
 #endif
         return false;
     }
-    contents->setParent(this);
+    contents->setParent(q);
 
     QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
     int status = attributes.value(QNetworkRequest::HttpStatusCodeAttribute).toInt();
     if (status < 100)
         status = 200;           // fake it
 
-    setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
-    setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
-    setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
+    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(),
@@ -955,16 +1229,19 @@ bool QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &m
 
     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(this, "metaDataChanged", Qt::QueuedConnection);
-    qRegisterMetaType<QIODevice*>("QIODevice*");
-    QMetaObject::invokeMethod(this, "writeDownstreamData", Qt::QueuedConnection, Q_ARG(QIODevice*, contents));
+    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";
+    qDebug() << "Successfully sent cache:" << url << contents->size() << "bytes";
 #endif
 
     // Set the following flag so we can ignore some signals from HTTP thread
@@ -973,50 +1250,49 @@ bool QNetworkAccessHttpBackend::sendCacheContents(const QNetworkCacheMetaData &m
     return true;
 }
 
-void QNetworkAccessHttpBackend::copyFinished(QIODevice *dev)
-{
-    delete dev;
-    finished();
-}
-
 #ifndef QT_NO_OPENSSL
-void QNetworkAccessHttpBackend::ignoreSslErrors()
+void QNetworkReplyHttpImpl::ignoreSslErrors()
 {
-    pendingIgnoreAllSslErrors = true;
+    Q_D(QNetworkReplyHttpImpl);
+
+    d->pendingIgnoreAllSslErrors = true;
 }
 
-void QNetworkAccessHttpBackend::ignoreSslErrors(const QList<QSslError> &errors)
+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.)
-    pendingIgnoreSslErrorsList = errors;
-}
-
-void QNetworkAccessHttpBackend::fetchSslConfiguration(QSslConfiguration &config) const
-{
-    if (pendingSslConfiguration)
-        config = *pendingSslConfiguration;
-    else
-        config = request().sslConfiguration();
+    d->pendingIgnoreSslErrorsList = errors;
 }
 
-void QNetworkAccessHttpBackend::setSslConfiguration(const QSslConfiguration &newconfig)
+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);
+    qDebug() << "sslConfigurationImplementation";
+    return d->sslConfiguration;
+}
 #endif
 
-QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetworkCacheMetaData &oldMetaData) const
+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 = rawHeaderList();
+    QList<QByteArray> newHeaders = q->rawHeaderList();
     foreach (QByteArray header, newHeaders) {
         QByteArray originalHeader = header;
         header = header.toLower();
@@ -1043,7 +1319,7 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo
 
         // Don't store Warning 1xx headers
         if (header == "warning") {
-            QByteArray v = rawHeader(header);
+            QByteArray v = q->rawHeader(header);
             if (v.length() == 3
                 && v[0] == '1'
                 && v[1] >= '0' && v[1] <= '9'
@@ -1076,7 +1352,7 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo
             qDebug() << "old" << o;
         }
 #endif
-        cacheHeaders.setRawHeader(originalHeader, rawHeader(header));
+        cacheHeaders.setRawHeader(originalHeader, q->rawHeader(header));
     }
     metaData.setRawHeaders(cacheHeaders.rawHeaders);
 
@@ -1154,37 +1430,532 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo
     return metaData;
 }
 
-bool QNetworkAccessHttpBackend::canResume() const
+bool QNetworkReplyHttpImplPrivate::canResume() const
 {
+    Q_Q(const QNetworkReplyHttpImpl);
+
     // Only GET operation supports resuming.
-    if (operation() != QNetworkAccessManager::GetOperation)
+    if (operation != QNetworkAccessManager::GetOperation)
         return false;
 
     // Can only resume if server/resource supports Range header.
     QByteArray acceptRangesheaderName("Accept-Ranges");
-    if (!hasRawHeader(acceptRangesheaderName) || rawHeader(acceptRangesheaderName) == "none")
+    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 (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 (usingZerocopyDownloadBuffer)
+    if (downloadZerocopyBuffer)
         return false;
 
     return true;
 }
 
-void QNetworkAccessHttpBackend::setResumeOffset(quint64 offset)
+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::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
index 712dd2f..476022d 100644 (file)
@@ -39,8 +39,8 @@
 **
 ****************************************************************************/
 
-#ifndef QNETWORKACCESSHTTPBACKEND_P_H
-#define QNETWORKACCESSHTTPBACKEND_P_H
+#ifndef QNETWORKREPLYHTTPIMPL_P_H
+#define QNETWORKREPLYHTTPIMPL_P_H
 
 //
 //  W A R N I N G
 // We mean it.
 //
 
-#include "qhttpnetworkconnection_p.h"
-#include "qnetworkaccessbackend_p.h"
 #include "qnetworkrequest.h"
 #include "qnetworkreply.h"
-#include "qabstractsocket.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 QNetworkAccessCachedHttpConnection;
-
-class QNetworkAccessHttpBackendIODevice;
+class QIODevice;
 
-class QNetworkAccessHttpBackend: public QNetworkAccessBackend
+class QNetworkReplyHttpImplPrivate;
+class QNetworkReplyHttpImpl: public QNetworkReply
 {
     Q_OBJECT
 public:
-    QNetworkAccessHttpBackend();
-    virtual ~QNetworkAccessHttpBackend();
-
-    virtual void open();
-    virtual void closeDownstreamChannel();
+    QNetworkReplyHttpImpl(QNetworkAccessManager* const, const QNetworkRequest&, QNetworkAccessManager::Operation&, QIODevice* outgoingData);
+    virtual ~QNetworkReplyHttpImpl();
 
-    virtual void downstreamReadyWrite();
-    virtual void setDownstreamLimited(bool b);
+    void close();
+    void abort();
+    qint64 bytesAvailable() const;
+    bool isSequential () const;
+    qint64 size() const;
+    qint64 readData(char*, qint64);
+    void setReadBufferSize(qint64 size);
+    bool canReadLine () const;
 
-    virtual void copyFinished(QIODevice *);
 #ifndef QT_NO_OPENSSL
-    virtual void ignoreSslErrors();
-    virtual void ignoreSslErrors(const QList<QSslError> &errors);
+    void ignoreSslErrors();
+    Q_INVOKABLE void ignoreSslErrorsImplementation(const QList<QSslError> &errors);
+    Q_INVOKABLE void setSslConfigurationImplementation(const QSslConfiguration &configuration);
+    Q_INVOKABLE QSslConfiguration sslConfigurationImplementation() const;
+#endif
+//    Q_INVOKABLE QSslConfiguration sslConfigurationImplementation() const;
+//    Q_INVOKABLE void setSslConfigurationImplementation(const QSslConfiguration &configuration);
 
-    virtual void fetchSslConfiguration(QSslConfiguration &configuration) const;
-    virtual void setSslConfiguration(const QSslConfiguration &configuration);
+    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
-    QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const;
+    Q_PRIVATE_SLOT(d_func(), void _q_finished())
+    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))
 
-    // we return true since HTTP needs to send PUT/POST data again after having authenticated
-    bool needsResetableUploadData() { return true; }
+    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))
 
-    bool canResume() const;
-    void setResumeOffset(quint64 offset);
 
 signals:
     // To HTTP thread:
@@ -109,57 +139,164 @@ signals:
     void startHttpRequestSynchronously();
 
     void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize);
-private slots:
-    // 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&);
+};
+
+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();
 
-    // From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread:
-    void resetUploadDataSlot(bool *r);
-    void wantUploadDataSlot(qint64);
-    void sentUploadDataSlot(qint64);
 
-    bool sendCacheContents(const QNetworkCacheMetaData &metaData);
+    // ?
+    void consume(qint64 count);
+
+    void setDownloadBuffer(QSharedPointer<char> sp, qint64 size);
+    char* getDownloadBuffer(qint64 size);
+
+    // FIXME
+    void finished();
+    void error(QNetworkReply::NetworkError code, const QString &errorString);
+    void metaDataChanged();
+    void redirectionRequested(const QUrl &target);
+
+
+    QNetworkAccessManager *manager;
+    QNetworkAccessManagerPrivate *managerPrivate;
+    QNetworkRequest request;
+    QNetworkAccessManager::Operation operation;
+
+    QNonContiguousByteDevice* createUploadByteDevice();
+    QSharedPointer<QNonContiguousByteDevice> uploadByteDevice;
+    QIODevice *outgoingData;
+    QSharedPointer<QRingBuffer> outgoingDataBuffer;
+    void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal); // dup?
+
+
+    bool migrateBackend();
+    quint64 resumeOffset;
+    bool canResume() const;
+    void setResumeOffset(quint64 offset);
+    qint64 bytesUploaded;
+    qint64 preMigrationDownloaded;
+
+    void createCache();
+    void completeCacheSave();
+    void setCachingEnabled(bool enable);
+    bool isCachingEnabled() const;
+    void initCacheSaveDevice();
+    QAbstractNetworkCache *networkCache() const;
+    QIODevice *cacheLoadDevice;
+    bool cacheEnabled; // is this for saving?
+    QIODevice *cacheSaveDevice;
+    bool loadingFromCache;
+
+
+    QUrl urlForLastAuthentication;
+#ifndef QT_NO_NETWORKPROXY
+    QNetworkProxy lastProxyAuthentication;
+    QList<QNetworkProxy> proxyList;
+#endif
 
-private:
-    QHttpNetworkRequest httpRequest; // There is also a copy in the HTTP thread
     int statusCode;
     QString reasonPhrase;
+
+    State state;
+
+
+    // Used for normal downloading. For "zero copy" the downloadZerocopyBuffer is used
+    QByteDataBuffer downloadMultiBuffer;
+    QByteDataBuffer pendingDownloadData; // For signal compression
+    qint64 bytesDownloaded;
+    qint64 lastBytesDownloaded;
+
+    // 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;
+
+
+    QHttpNetworkRequest httpRequest; // There is also a copy in the HTTP thread
+
     // Will be increased by HTTP thread:
     QSharedPointer<QAtomicInt> pendingDownloadDataEmissions;
     QSharedPointer<QAtomicInt> pendingDownloadProgressEmissions;
-    bool loadingFromCache;
-    QByteDataBuffer pendingDownloadData;
-    bool usingZerocopyDownloadBuffer;
+
+    bool synchronous;
 
 #ifndef QT_NO_OPENSSL
-    QSslConfiguration *pendingSslConfiguration;
+    QSslConfiguration sslConfiguration;
     bool pendingIgnoreAllSslErrors;
     QList<QSslError> pendingIgnoreSslErrorsList;
 #endif
 
-    quint64 resumeOffset;
 
     void validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache);
     void invalidateCache();
+    bool sendCacheContents(const QNetworkCacheMetaData &metaData);
+    QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const;
+
+
     void postRequest();
-    void readFromHttp();
+
+
     void checkForRedirect(const int statusCode);
-};
 
-class QNetworkAccessHttpBackendFactory : public QNetworkAccessBackendFactory
-{
 public:
-    virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
-                                          const QNetworkRequest &request) const;
+    // 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
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