1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qnetworkreplyimpl_p.h"
43 #include "qnetworkaccessbackend_p.h"
44 #include "qnetworkcookie.h"
45 #include "qnetworkcookiejar.h"
46 #include "qabstractnetworkcache.h"
47 #include "QtCore/qcoreapplication.h"
48 #include "QtCore/qdatetime.h"
49 #include "QtNetwork/qsslconfiguration.h"
50 #include "QtNetwork/qnetworksession.h"
51 #include "qnetworkaccessmanager_p.h"
53 #include <QtCore/QCoreApplication>
55 Q_DECLARE_METATYPE(QSharedPointer<char>)
59 inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
60 : backend(0), outgoingData(0),
62 cacheEnabled(false), cacheSaveDevice(0),
63 notificationHandlingPaused(false),
64 bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
67 , downloadBufferReadPosition(0)
68 , downloadBufferCurrentSize(0)
69 , downloadBufferMaximumSize(0)
74 void QNetworkReplyImplPrivate::_q_startOperation()
76 Q_Q(QNetworkReplyImpl);
78 // ensure this function is only being called once
79 if (state == Working || state == Finished) {
80 qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
85 // note: if that method is called directly, it cannot happen that the backend is 0,
86 // because we just checked via a qobject_cast that we got a http backend (see
87 // QNetworkReplyImplPrivate::setup())
89 error(QNetworkReplyImpl::ProtocolUnknownError,
90 QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;
95 #ifndef QT_NO_BEARERMANAGEMENT
96 // Do not start background requests if they are not allowed by session policy
97 QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
98 QVariant isBackground = backend->request().attribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(false));
99 if (isBackground.toBool() && session && session->usagePolicies().testFlag(QNetworkSession::NoBackgroundTrafficPolicy)) {
100 error(QNetworkReply::BackgroundRequestNotAllowedError,
101 QCoreApplication::translate("QNetworkReply", "Background request not allowed."));
107 if (!backend->start()) {
108 #ifndef QT_NO_BEARERMANAGEMENT
109 // backend failed to start because the session state is not Connected.
110 // QNetworkAccessManager will call _q_startOperation again for us when the session
112 state = WaitingForSession;
115 QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)),
116 q, SLOT(_q_networkSessionFailed()));
118 if (!session->isOpen()) {
119 session->setSessionProperty(QStringLiteral("ConnectInBackground"), isBackground);
123 qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
125 error(QNetworkReplyImpl::NetworkSessionFailedError,
126 QCoreApplication::translate("QNetworkReply", "Network session error."));
130 qWarning("Backend start failed");
132 error(QNetworkReplyImpl::UnknownNetworkError,
133 QCoreApplication::translate("QNetworkReply", "backend start error."));
139 #ifndef QT_NO_BEARERMANAGEMENT
141 //get notification of policy changes.
142 QObject::connect(session.data(), SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)),
143 q, SLOT(_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies)));
147 // Prepare timer for progress notifications
148 downloadProgressSignalChoke.start();
149 uploadProgressSignalChoke.invalidate();
151 if (backend && backend->isSynchronous()) {
153 q_func()->setFinished(true);
155 if (state != Finished) {
156 if (operation == QNetworkAccessManager::GetOperation)
157 pendingNotifications.append(NotifyDownstreamReadyWrite);
159 handleNotifications();
164 void QNetworkReplyImplPrivate::_q_copyReadyRead()
166 Q_Q(QNetworkReplyImpl);
167 if (state != Working)
169 if (!copyDevice || !q->isOpen())
172 // FIXME Optimize to use download buffer if it is a QBuffer.
173 // Needs to be done where sendCacheContents() (?) of HTTP is emitting
177 qint64 bytesToRead = nextDownstreamBlockSize();
178 if (bytesToRead == 0)
179 // we'll be called again, eventually
182 bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
184 byteData.resize(bytesToRead);
185 qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
186 if (bytesActuallyRead == -1) {
188 backendNotify(NotifyCopyFinished);
192 byteData.resize(bytesActuallyRead);
193 readBuffer.append(byteData);
195 if (!copyDevice->isSequential() && copyDevice->atEnd()) {
196 backendNotify(NotifyCopyFinished);
197 bytesDownloaded += bytesActuallyRead;
201 bytesDownloaded += bytesActuallyRead;
204 if (bytesDownloaded == lastBytesDownloaded) {
205 // we didn't read anything
209 lastBytesDownloaded = bytesDownloaded;
210 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
211 if (preMigrationDownloaded != Q_INT64_C(-1))
212 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
213 pauseNotificationHandling();
214 // emit readyRead before downloadProgress incase this will cause events to be
215 // processed and we get into a recursive call (as in QProgressDialog).
217 if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
218 downloadProgressSignalChoke.restart();
219 emit q->downloadProgress(bytesDownloaded,
220 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
222 resumeNotificationHandling();
225 void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
230 void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
232 Q_Q(QNetworkReplyImpl);
234 // make sure this is only called once, ever.
235 //_q_bufferOutgoingData may call it or the readChannelFinished emission
236 if (state != Buffering)
239 // disconnect signals
240 QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
241 QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
243 // finally, start the request
244 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
247 void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
249 Q_Q(QNetworkReplyImpl);
251 if (!outgoingDataBuffer) {
252 // first call, create our buffer
253 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
255 QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
256 QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
259 qint64 bytesBuffered = 0;
260 qint64 bytesToBuffer = 0;
262 // read data into our buffer
264 bytesToBuffer = outgoingData->bytesAvailable();
265 // unknown? just try 2 kB, this also ensures we always try to read the EOF
266 if (bytesToBuffer <= 0)
267 bytesToBuffer = 2*1024;
269 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
270 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
272 if (bytesBuffered == -1) {
273 // EOF has been reached.
274 outgoingDataBuffer->chop(bytesToBuffer);
276 _q_bufferOutgoingDataFinished();
278 } else if (bytesBuffered == 0) {
279 // nothing read right now, just wait until we get called again
280 outgoingDataBuffer->chop(bytesToBuffer);
284 // don't break, try to read() again
285 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
290 #ifndef QT_NO_BEARERMANAGEMENT
291 void QNetworkReplyImplPrivate::_q_networkSessionConnected()
293 Q_Q(QNetworkReplyImpl);
295 if (manager.isNull())
298 QSharedPointer<QNetworkSession> session = manager->d_func()->getNetworkSession();
302 if (session->state() != QNetworkSession::Connected)
306 case QNetworkReplyImplPrivate::Buffering:
307 case QNetworkReplyImplPrivate::Working:
308 case QNetworkReplyImplPrivate::Reconnecting:
309 // Migrate existing downloads to new network connection.
312 case QNetworkReplyImplPrivate::WaitingForSession:
313 // Start waiting requests.
314 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
321 void QNetworkReplyImplPrivate::_q_networkSessionFailed()
323 // Abort waiting and working replies.
324 if (state == WaitingForSession || state == Working) {
326 QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
329 errorStr = session->errorString();
331 errorStr = QCoreApplication::translate("QNetworkReply", "Network session error.");
332 error(QNetworkReplyImpl::NetworkSessionFailedError, errorStr);
337 void QNetworkReplyImplPrivate::_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies newPolicies)
339 if (backend->request().attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) {
340 if (newPolicies & QNetworkSession::NoBackgroundTrafficPolicy) {
341 // Abort waiting and working replies.
342 if (state == WaitingForSession || state == Working) {
344 error(QNetworkReply::BackgroundRequestNotAllowedError,
345 QCoreApplication::translate("QNetworkReply", "Background request not allowed."));
348 // ### if backend->canResume(), then we could resume automatically, however no backend supports resuming
354 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
357 Q_Q(QNetworkReplyImpl);
364 q->QIODevice::open(QIODevice::ReadOnly);
365 // Internal code that does a HTTP reply for the synchronous Ajax
367 QVariant synchronousHttpAttribute = req.attribute(
368 static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
369 // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
370 // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
371 if (synchronousHttpAttribute.toBool() && outgoingData) {
372 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
373 qint64 previousDataSize = 0;
375 previousDataSize = outgoingDataBuffer->size();
376 outgoingDataBuffer->append(outgoingData->readAll());
377 } while (outgoingDataBuffer->size() != previousDataSize);
381 backend->setSynchronous(synchronousHttpAttribute.toBool());
384 if (outgoingData && backend && !backend->isSynchronous()) {
385 // there is data to be uploaded, e.g. HTTP POST.
387 if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
388 // backend does not need upload buffering or
389 // fixed size non-sequential
390 // just start the operation
391 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
393 bool bufferingDisallowed =
394 req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
397 if (bufferingDisallowed) {
398 // if a valid content-length header for the request was supplied, we can disable buffering
399 // if not, we will buffer anyway
400 if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
401 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
404 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
407 // _q_startOperation will be called when the buffering has finished.
409 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
413 // for HTTP, we want to send out the request as fast as possible to the network, without
414 // invoking methods in a QueuedConnection
416 if (backend && backend->isSynchronous()) {
419 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
422 if (backend && backend->isSynchronous())
425 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
430 void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
432 Q_Q(QNetworkReplyImpl);
433 if (!pendingNotifications.contains(notification))
434 pendingNotifications.enqueue(notification);
436 if (pendingNotifications.size() == 1)
437 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
440 void QNetworkReplyImplPrivate::handleNotifications()
442 if (notificationHandlingPaused)
445 NotificationQueue current = pendingNotifications;
446 pendingNotifications.clear();
448 if (state != Working)
451 while (state == Working && !current.isEmpty()) {
452 InternalNotifications notification = current.dequeue();
453 switch (notification) {
454 case NotifyDownstreamReadyWrite:
458 backend->downstreamReadyWrite();
461 case NotifyCloseDownstreamChannel:
462 backend->closeDownstreamChannel();
465 case NotifyCopyFinished: {
466 QIODevice *dev = copyDevice;
468 backend->copyFinished(dev);
475 // Do not handle the notifications while we are emitting downloadProgress
477 void QNetworkReplyImplPrivate::pauseNotificationHandling()
479 notificationHandlingPaused = true;
482 // Resume notification handling
483 void QNetworkReplyImplPrivate::resumeNotificationHandling()
485 Q_Q(QNetworkReplyImpl);
486 notificationHandlingPaused = false;
487 if (pendingNotifications.size() >= 1)
488 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
491 QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
495 return backend->networkCache();
498 void QNetworkReplyImplPrivate::createCache()
500 // check if we can save and if we're allowed to
502 || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool())
507 bool QNetworkReplyImplPrivate::isCachingEnabled() const
509 return (cacheEnabled && networkCache() != 0);
512 void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
514 if (!enable && !cacheEnabled)
515 return; // nothing to do
516 if (enable && cacheEnabled)
517 return; // nothing to do either!
520 if (bytesDownloaded) {
521 // refuse to enable in this case
522 qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
528 // someone told us to turn on, then back off?
529 // ok... but you should make up your mind
530 qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
531 "backend %s probably needs to be fixed",
532 backend->metaObject()->className());
533 networkCache()->remove(url);
535 cacheEnabled = false;
539 void QNetworkReplyImplPrivate::completeCacheSave()
541 if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
542 networkCache()->remove(url);
543 } else if (cacheEnabled && cacheSaveDevice) {
544 networkCache()->insert(cacheSaveDevice);
547 cacheEnabled = false;
550 void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
552 Q_Q(QNetworkReplyImpl);
553 bytesUploaded = bytesSent;
555 //choke signal emissions, except the first and last signals which are unconditional
556 if (uploadProgressSignalChoke.isValid()) {
557 if (bytesSent != bytesTotal && uploadProgressSignalChoke.elapsed() < progressSignalInterval) {
560 uploadProgressSignalChoke.restart();
562 uploadProgressSignalChoke.start();
565 pauseNotificationHandling();
566 emit q->uploadProgress(bytesSent, bytesTotal);
567 resumeNotificationHandling();
571 qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
573 enum { DesiredBufferSize = 32 * 1024 };
574 if (readBufferMaxSize == 0)
575 return DesiredBufferSize;
577 return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
580 void QNetworkReplyImplPrivate::initCacheSaveDevice()
582 Q_Q(QNetworkReplyImpl);
584 // The disk cache does not support partial content, so don't even try to
585 // save any such content into the cache.
586 if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
587 cacheEnabled = false;
591 // save the meta data
592 QNetworkCacheMetaData metaData;
593 metaData.setUrl(url);
594 metaData = backend->fetchCacheMetaData(metaData);
596 // save the redirect request also in the cache
597 QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
598 if (redirectionTarget.isValid()) {
599 QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
600 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
601 metaData.setAttributes(attributes);
604 cacheSaveDevice = networkCache()->prepare(metaData);
606 if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
607 if (cacheSaveDevice && !cacheSaveDevice->isOpen())
608 qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
609 "class %s probably needs to be fixed",
610 networkCache()->metaObject()->className());
612 networkCache()->remove(url);
614 cacheEnabled = false;
618 // we received downstream data and send this to the cache
619 // and to our readBuffer (which in turn gets read by the user of QNetworkReply)
620 void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
622 Q_Q(QNetworkReplyImpl);
626 if (cacheEnabled && !cacheSaveDevice) {
627 initCacheSaveDevice();
630 qint64 bytesWritten = 0;
631 for (int i = 0; i < data.bufferCount(); i++) {
632 QByteArray const &item = data[i];
635 cacheSaveDevice->write(item.constData(), item.size());
636 readBuffer.append(item);
638 bytesWritten += item.size();
642 bytesDownloaded += bytesWritten;
643 lastBytesDownloaded = bytesDownloaded;
645 appendDownstreamDataSignalEmissions();
648 void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
650 Q_Q(QNetworkReplyImpl);
652 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
653 if (preMigrationDownloaded != Q_INT64_C(-1))
654 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
655 pauseNotificationHandling();
656 // important: At the point of this readyRead(), the data parameter list must be empty,
657 // else implicit sharing will trigger memcpy when the user is reading data!
659 // emit readyRead before downloadProgress incase this will cause events to be
660 // processed and we get into a recursive call (as in QProgressDialog).
661 if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
662 downloadProgressSignalChoke.restart();
663 emit q->downloadProgress(bytesDownloaded,
664 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
667 resumeNotificationHandling();
668 // do we still have room in the buffer?
669 if (nextDownstreamBlockSize() > 0)
670 backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
673 // this is used when it was fetched from the cache, right?
674 void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
676 Q_Q(QNetworkReplyImpl);
680 // read until EOF from data
682 qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
683 "backend probly needs to be fixed");
688 q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
689 q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
695 void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
702 qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
705 static void downloadBufferDeleter(char *ptr)
710 char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
712 Q_Q(QNetworkReplyImpl);
714 if (!downloadBuffer) {
715 // We are requested to create it
716 // Check attribute() if allocating a buffer of that size can be allowed
717 QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
718 if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
719 downloadBufferCurrentSize = 0;
720 downloadBufferMaximumSize = size;
721 downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
722 downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
724 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
728 return downloadBuffer;
731 void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
733 Q_Q(QNetworkReplyImpl);
735 downloadBufferPointer = sp;
736 downloadBuffer = downloadBufferPointer.data();
737 downloadBufferCurrentSize = 0;
738 downloadBufferMaximumSize = size;
739 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
743 void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
745 Q_Q(QNetworkReplyImpl);
749 if (cacheEnabled && !cacheSaveDevice)
750 initCacheSaveDevice();
752 if (cacheSaveDevice && bytesReceived == bytesTotal) {
753 // if (lastBytesDownloaded == -1)
754 // lastBytesDownloaded = 0;
755 // cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
757 // Write everything in one go if we use a download buffer. might be more performant.
758 cacheSaveDevice->write(downloadBuffer, bytesTotal);
761 bytesDownloaded = bytesReceived;
762 lastBytesDownloaded = bytesReceived;
764 downloadBufferCurrentSize = bytesReceived;
766 // Only emit readyRead when actual data is there
767 // emit readyRead before downloadProgress incase this will cause events to be
768 // processed and we get into a recursive call (as in QProgressDialog).
769 if (bytesDownloaded > 0)
771 if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
772 downloadProgressSignalChoke.restart();
773 emit q->downloadProgress(bytesDownloaded, bytesTotal);
777 void QNetworkReplyImplPrivate::finished()
779 Q_Q(QNetworkReplyImpl);
781 if (state == Finished || state == Aborted || state == WaitingForSession)
784 pauseNotificationHandling();
785 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
786 if (preMigrationDownloaded != Q_INT64_C(-1))
787 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
789 if (!manager.isNull()) {
790 #ifndef QT_NO_BEARERMANAGEMENT
791 QSharedPointer<QNetworkSession> session (manager->d_func()->getNetworkSession());
792 if (session && session->state() == QNetworkSession::Roaming &&
793 state == Working && errorCode != QNetworkReply::OperationCanceledError) {
794 // only content with a known size will fail with a temporary network failure error
795 if (!totalSize.isNull()) {
796 if (bytesDownloaded != totalSize) {
797 if (migrateBackend()) {
798 // either we are migrating or the request is finished/aborted
799 if (state == Reconnecting || state == WaitingForSession) {
800 resumeNotificationHandling();
801 return; // exit early if we are migrating.
804 error(QNetworkReply::TemporaryNetworkFailureError,
805 QNetworkReply::tr("Temporary network failure."));
812 resumeNotificationHandling();
815 q->setFinished(true);
817 pendingNotifications.clear();
819 pauseNotificationHandling();
820 if (totalSize.isNull() || totalSize == -1) {
821 emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
823 emit q->downloadProgress(bytesDownloaded, totalSize.toLongLong());
826 if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
827 emit q->uploadProgress(0, 0);
828 resumeNotificationHandling();
830 // if we don't know the total size of or we received everything save the cache
831 if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
834 // note: might not be a good idea, since users could decide to delete us
835 // which would delete the backend too...
836 // maybe we should protect the backend
837 pauseNotificationHandling();
838 emit q->readChannelFinished();
840 resumeNotificationHandling();
843 void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
845 Q_Q(QNetworkReplyImpl);
846 // Can't set and emit multiple errors.
847 if (errorCode != QNetworkReply::NoError) {
848 qWarning( "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
853 q->setErrorString(errorMessage);
855 // note: might not be a good idea, since users could decide to delete us
856 // which would delete the backend too...
857 // maybe we should protect the backend
861 void QNetworkReplyImplPrivate::metaDataChanged()
863 Q_Q(QNetworkReplyImpl);
864 // 1. do we have cookies?
865 // 2. are we allowed to set them?
866 if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()
867 && (static_cast<QNetworkRequest::LoadControl>
868 (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
869 QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
870 QList<QNetworkCookie> cookies =
871 qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
872 QNetworkCookieJar *jar = manager->cookieJar();
874 jar->setCookiesFromUrl(cookies, url);
876 emit q->metaDataChanged();
879 void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
881 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
884 void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
887 Q_Q(QNetworkReplyImpl);
888 emit q->sslErrors(errors);
894 QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
895 : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
899 QNetworkReplyImpl::~QNetworkReplyImpl()
901 Q_D(QNetworkReplyImpl);
903 // This code removes the data from the cache if it was prematurely aborted.
904 // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
905 // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
906 if (d->isCachingEnabled())
907 d->networkCache()->remove(url());
910 void QNetworkReplyImpl::abort()
912 Q_D(QNetworkReplyImpl);
913 if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
916 // stop both upload and download
918 disconnect(d->outgoingData, 0, this, 0);
920 disconnect(d->copyDevice, 0, this, 0);
922 QNetworkReply::close();
924 if (d->state != QNetworkReplyImplPrivate::Finished) {
925 // call finished which will emit signals
926 d->error(OperationCanceledError, tr("Operation canceled"));
927 if (d->state == QNetworkReplyImplPrivate::WaitingForSession)
928 d->state = QNetworkReplyImplPrivate::Working;
931 d->state = QNetworkReplyImplPrivate::Aborted;
933 // finished may access the backend
935 d->backend->deleteLater();
940 void QNetworkReplyImpl::close()
942 Q_D(QNetworkReplyImpl);
943 if (d->state == QNetworkReplyImplPrivate::Aborted ||
944 d->state == QNetworkReplyImplPrivate::Finished)
949 d->backend->closeDownstreamChannel();
951 disconnect(d->copyDevice, 0, this, 0);
953 QNetworkReply::close();
955 // call finished which will emit signals
956 d->error(OperationCanceledError, tr("Operation canceled"));
960 bool QNetworkReplyImpl::canReadLine () const
962 Q_D(const QNetworkReplyImpl);
963 return QNetworkReply::canReadLine() || d->readBuffer.canReadLine();
968 Returns the number of bytes available for reading with
969 QIODevice::read(). The number of bytes available may grow until
970 the finished() signal is emitted.
972 qint64 QNetworkReplyImpl::bytesAvailable() const
974 // Special case for the "zero copy" download buffer
975 Q_D(const QNetworkReplyImpl);
976 if (d->downloadBuffer) {
977 qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
978 return QNetworkReply::bytesAvailable() + maxAvail;
981 return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
984 void QNetworkReplyImpl::setReadBufferSize(qint64 size)
986 Q_D(QNetworkReplyImpl);
987 if (size > d->readBufferMaxSize &&
988 size > d->readBuffer.byteAmount())
989 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
991 QNetworkReply::setReadBufferSize(size);
994 d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
998 void QNetworkReplyImpl::sslConfigurationImplementation(QSslConfiguration &configuration) const
1000 Q_D(const QNetworkReplyImpl);
1002 d->backend->fetchSslConfiguration(configuration);
1005 void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
1007 Q_D(QNetworkReplyImpl);
1008 if (d->backend && !config.isNull())
1009 d->backend->setSslConfiguration(config);
1012 void QNetworkReplyImpl::ignoreSslErrors()
1014 Q_D(QNetworkReplyImpl);
1016 d->backend->ignoreSslErrors();
1019 void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
1021 Q_D(QNetworkReplyImpl);
1023 d->backend->ignoreSslErrors(errors);
1030 qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
1032 Q_D(QNetworkReplyImpl);
1034 // Special case code if we have the "zero copy" download buffer
1035 if (d->downloadBuffer) {
1036 qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
1038 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
1039 // FIXME what about "Aborted" state?
1040 memcpy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
1041 d->downloadBufferReadPosition += maxAvail;
1046 if (d->readBuffer.isEmpty())
1047 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
1048 // FIXME what about "Aborted" state?
1050 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
1052 // optimization for getChar()
1053 *data = d->readBuffer.getChar();
1057 maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
1058 return d->readBuffer.read(data, maxlen);
1062 \internal Reimplemented for internal purposes
1064 bool QNetworkReplyImpl::event(QEvent *e)
1066 if (e->type() == QEvent::NetworkReplyUpdated) {
1067 d_func()->handleNotifications();
1071 return QObject::event(e);
1075 Migrates the backend of the QNetworkReply to a new network connection if required. Returns
1076 true if the reply is migrated or it is not required; otherwise returns false.
1078 bool QNetworkReplyImplPrivate::migrateBackend()
1080 Q_Q(QNetworkReplyImpl);
1082 // Network reply is already finished or aborted, don't need to migrate.
1083 if (state == Finished || state == Aborted)
1086 // Request has outgoing data, not migrating.
1090 // Request is serviced from the cache, don't need to migrate.
1094 // Backend does not support resuming download.
1095 if (!backend->canResume())
1098 state = QNetworkReplyImplPrivate::Reconnecting;
1105 cookedHeaders.clear();
1108 preMigrationDownloaded = bytesDownloaded;
1110 backend = manager->d_func()->findBackend(operation, request);
1113 backend->setParent(q);
1114 backend->reply = this;
1115 backend->setResumeOffset(bytesDownloaded);
1119 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1121 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1122 #endif // QT_NO_HTTP
1127 #ifndef QT_NO_BEARERMANAGEMENT
1128 QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent,
1129 const QNetworkRequest &req,
1130 QNetworkAccessManager::Operation op)
1131 : QNetworkReply(parent)
1137 qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
1139 QString msg = QCoreApplication::translate("QNetworkAccessManager",
1140 "Network access is disabled.");
1141 setError(UnknownNetworkError, msg);
1143 QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
1144 Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
1145 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
1148 QDisabledNetworkReply::~QDisabledNetworkReply()
1155 #include "moc_qnetworkreplyimpl_p.cpp"