summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Goetz <Markus.Goetz@nokia.com>2011-03-28 14:56:26 +0200
committerMarkus Goetz <Markus.Goetz@nokia.com>2011-05-03 16:34:54 +0200
commita0beeac097db8ead6f2a56e21699f6007b9686d4 (patch)
tree1558fc12d013ca41be543618bc9d4f7d5b3bdf9a
parentf78640e5c2274ecd4ccda1aba7607003c5bf00bf (diff)
QNAM: Start new HTTP backend architecture
-rw-r--r--src/network/access/access.pri4
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp32
-rw-r--r--src/network/access/qnetworkaccessmanager.h1
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp1191
-rw-r--r--src/network/access/qnetworkreplyhttpimpl_p.h247
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp9
6 files changed, 1203 insertions, 281 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index 5ead3ad37f..0f901b873d 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -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 \
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 34ac17037b..090a25c7f0 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -67,13 +67,12 @@
#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);
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index 47760b210b..b22be91a96 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -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())
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index e75347f78a..d38850b450 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -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
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
index 712dd2ff64..476022d7f7 100644
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
@@ -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
@@ -53,53 +53,83 @@
// 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
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index 9eb505d1b7..78e246315c 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -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