From cc1268a6cbc9797e2a9137f7a0389f3d144a8c0a Mon Sep 17 00:00:00 2001 From: Aaron McCarthy Date: Fri, 12 Feb 2010 11:14:49 +1000 Subject: Move http resume code into http backend. Adds two new virtual functions to QNetworkAccessBackend: bool canResume() const; and void setResumeOffset(quint64) canResume() is used to query the existing backend if resuming is supported for the current request. setResumeOffset() is used to set an additional offset which should be applied when the backend posts the request. The default implementation of canResume() returns false (resuming not supported). The default implementation of setResumeOffset() does nothing. --- src/network/access/qnetworkaccessbackend_p.h | 4 ++ src/network/access/qnetworkaccesshttpbackend.cpp | 48 ++++++++++++++++++++++++ src/network/access/qnetworkaccesshttpbackend_p.h | 5 +++ src/network/access/qnetworkreplyimpl.cpp | 21 ++--------- 4 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h index 830ec7e..eab011c 100644 --- a/src/network/access/qnetworkaccessbackend_p.h +++ b/src/network/access/qnetworkaccessbackend_p.h @@ -162,6 +162,10 @@ public: // This will possibly enable buffering of the upload data. virtual bool needsResetableUploadData() { return false; } + // Returns true if backend is able to resume downloads. + virtual bool canResume() const { return false; } + virtual void setResumeOffset(quint64 offset) { Q_UNUSED(offset); } + protected: // Create the device used for reading the upload data QNonContiguousByteDevice* createUploadByteDevice(); diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index 8e02723..aa6a820 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -296,6 +296,7 @@ QNetworkAccessHttpBackend::QNetworkAccessHttpBackend() #ifndef QT_NO_OPENSSL , pendingSslConfiguration(0), pendingIgnoreAllSslErrors(false) #endif + , resumeOffset(0) { } @@ -533,6 +534,28 @@ void QNetworkAccessHttpBackend::postRequest() httpRequest.setUrl(url()); QList headers = request().rawHeaderList(); + if (resumeOffset != 0) { + if (headers.contains("Range")) { + // Need to adjust resume offset for user specified range + + headers.removeOne("Range"); + + // We've already verified that requestRange starts with "bytes=", see canResume. + QByteArray requestRange = request().rawHeader("Range").mid(6); + + int index = requestRange.indexOf('-'); + + quint64 requestStartOffset = requestRange.left(index).toULongLong(); + quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong(); + + requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) + + '-' + QByteArray::number(requestEndOffset); + + httpRequest.setHeaderField("Range", requestRange); + } else { + httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-'); + } + } foreach (const QByteArray &header, headers) httpRequest.setHeaderField(header, request().rawHeader(header)); @@ -1108,6 +1131,31 @@ QNetworkCacheMetaData QNetworkAccessHttpBackend::fetchCacheMetaData(const QNetwo return metaData; } +bool QNetworkAccessHttpBackend::canResume() const +{ + // Only GET operation supports resuming. + if (operation() != QNetworkAccessManager::GetOperation) + return false; + + // Can only resume if server/resource supports Range header. + if (httpReply->headerField("Accept-Ranges", "none") == "none") + return false; + + // We only support resuming for byte ranges. + if (request().hasRawHeader("Range")) { + QByteArray range = request().rawHeader("Range"); + if (!range.startsWith("bytes=")) + return false; + } + + return true; +} + +void QNetworkAccessHttpBackend::setResumeOffset(quint64 offset) +{ + resumeOffset = offset; +} + QT_END_NAMESPACE #endif // QT_NO_HTTP diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h index 0eaf003..e5cc0ab 100644 --- a/src/network/access/qnetworkaccesshttpbackend_p.h +++ b/src/network/access/qnetworkaccesshttpbackend_p.h @@ -99,6 +99,9 @@ public: // we return true since HTTP needs to send PUT/POST data again after having authenticated bool needsResetableUploadData() { return true; } + bool canResume() const; + void setResumeOffset(quint64 offset); + private slots: void replyReadyRead(); void replyFinished(); @@ -120,6 +123,8 @@ private: QList pendingIgnoreSslErrorsList; #endif + quint64 resumeOffset; + void disconnectFromHttp(); void setupConnection(); void validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache); diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index 7f66b25..2906caa 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -497,15 +497,6 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data) QPointer qq = q; QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); - if (totalSize.isNull()) { - RawHeadersList::ConstIterator it = findRawHeader("Content-Range"); - if (it != rawHeaders.constEnd()) { - int index = it->second.lastIndexOf('/'); - if (index != -1) - totalSize = it->second.mid(index + 1).toLongLong() - preMigrationDownloaded; - } - } - if (preMigrationDownloaded != Q_INT64_C(-1)) totalSize = totalSize.toLongLong() + preMigrationDownloaded; pauseNotificationHandling(); @@ -812,8 +803,8 @@ bool QNetworkReplyImplPrivate::migrateBackend() if (state == Finished || state == Aborted) return true; - // Resume only supported by http backend, not migrating. - if (!qobject_cast(backend)) + // Backend does not support resuming download. + if (!backend->canResume()) return false; // Request has outgoing data, not migrating. @@ -824,11 +815,6 @@ bool QNetworkReplyImplPrivate::migrateBackend() if (copyDevice) return true; - // Range header is not supported by server/resource, can't migrate. - RawHeadersList::ConstIterator it = findRawHeader("Accept-Ranges"); - if (it == rawHeaders.constEnd() || it->second == "none") - return false; - state = QNetworkReplyImplPrivate::Reconnecting; if (backend) { @@ -841,13 +827,12 @@ bool QNetworkReplyImplPrivate::migrateBackend() preMigrationDownloaded = bytesDownloaded; - request.setRawHeader("Range", "bytes=" + QByteArray::number(preMigrationDownloaded) + '-'); - backend = manager->d_func()->findBackend(operation, request); if (backend) { backend->setParent(q); backend->reply = this; + backend->setResumeOffset(bytesDownloaded); } if (qobject_cast(backend)) { -- cgit v0.12