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 // ensure this function is only being called once
77 if (state == Working || state == Finished) {
78 qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
83 // note: if that method is called directly, it cannot happen that the backend is 0,
84 // because we just checked via a qobject_cast that we got a http backend (see
85 // QNetworkReplyImplPrivate::setup())
87 error(QNetworkReplyImpl::ProtocolUnknownError,
88 QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;
93 #ifndef QT_NO_BEARERMANAGEMENT
94 // Do not start background requests if they are not allowed by session policy
95 QSharedPointer<QNetworkSession> session(manager->d_func()->networkSession);
96 QVariant isBackground = backend->request().attribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(false));
97 if (isBackground.toBool() && session && session->usagePolicies().testFlag(QNetworkSession::NoBackgroundTrafficPolicy)) {
98 error(QNetworkReply::BackgroundRequestNotAllowedError,
99 QCoreApplication::translate("QNetworkReply", "Background request not allowed."));
105 if (!backend->start()) {
106 #ifndef QT_NO_BEARERMANAGEMENT
107 // backend failed to start because the session state is not Connected.
108 // QNetworkAccessManager will call _q_startOperation again for us when the session
110 state = WaitingForSession;
113 Q_Q(QNetworkReplyImpl);
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 if (backend && backend->isSynchronous()) {
141 q_func()->setFinished(true);
143 if (state != Finished) {
144 if (operation == QNetworkAccessManager::GetOperation)
145 pendingNotifications.append(NotifyDownstreamReadyWrite);
147 handleNotifications();
152 void QNetworkReplyImplPrivate::_q_copyReadyRead()
154 Q_Q(QNetworkReplyImpl);
155 if (state != Working)
157 if (!copyDevice || !q->isOpen())
160 // FIXME Optimize to use download buffer if it is a QBuffer.
161 // Needs to be done where sendCacheContents() (?) of HTTP is emitting
165 qint64 bytesToRead = nextDownstreamBlockSize();
166 if (bytesToRead == 0)
167 // we'll be called again, eventually
170 bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
172 byteData.resize(bytesToRead);
173 qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
174 if (bytesActuallyRead == -1) {
176 backendNotify(NotifyCopyFinished);
180 byteData.resize(bytesActuallyRead);
181 readBuffer.append(byteData);
183 if (!copyDevice->isSequential() && copyDevice->atEnd()) {
184 backendNotify(NotifyCopyFinished);
185 bytesDownloaded += bytesActuallyRead;
189 bytesDownloaded += bytesActuallyRead;
192 if (bytesDownloaded == lastBytesDownloaded) {
193 // we didn't read anything
197 lastBytesDownloaded = bytesDownloaded;
198 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
199 if (preMigrationDownloaded != Q_INT64_C(-1))
200 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
201 pauseNotificationHandling();
202 // emit readyRead before downloadProgress incase this will cause events to be
203 // processed and we get into a recursive call (as in QProgressDialog).
205 emit q->downloadProgress(bytesDownloaded,
206 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
207 resumeNotificationHandling();
210 void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
215 void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
217 Q_Q(QNetworkReplyImpl);
219 // make sure this is only called once, ever.
220 //_q_bufferOutgoingData may call it or the readChannelFinished emission
221 if (state != Buffering)
224 // disconnect signals
225 QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
226 QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
228 // finally, start the request
229 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
232 void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
234 Q_Q(QNetworkReplyImpl);
236 if (!outgoingDataBuffer) {
237 // first call, create our buffer
238 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
240 QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
241 QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
244 qint64 bytesBuffered = 0;
245 qint64 bytesToBuffer = 0;
247 // read data into our buffer
249 bytesToBuffer = outgoingData->bytesAvailable();
250 // unknown? just try 2 kB, this also ensures we always try to read the EOF
251 if (bytesToBuffer <= 0)
252 bytesToBuffer = 2*1024;
254 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
255 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
257 if (bytesBuffered == -1) {
258 // EOF has been reached.
259 outgoingDataBuffer->chop(bytesToBuffer);
261 _q_bufferOutgoingDataFinished();
263 } else if (bytesBuffered == 0) {
264 // nothing read right now, just wait until we get called again
265 outgoingDataBuffer->chop(bytesToBuffer);
269 // don't break, try to read() again
270 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
275 #ifndef QT_NO_BEARERMANAGEMENT
276 void QNetworkReplyImplPrivate::_q_networkSessionConnected()
278 Q_Q(QNetworkReplyImpl);
280 if (manager.isNull())
283 QNetworkSession *session = manager->d_func()->networkSession.data();
287 if (session->state() != QNetworkSession::Connected)
291 case QNetworkReplyImplPrivate::Buffering:
292 case QNetworkReplyImplPrivate::Working:
293 case QNetworkReplyImplPrivate::Reconnecting:
294 // Migrate existing downloads to new network connection.
297 case QNetworkReplyImplPrivate::WaitingForSession:
298 // Start waiting requests.
299 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
306 void QNetworkReplyImplPrivate::_q_networkSessionFailed()
308 // Abort waiting and working replies.
309 if (state == WaitingForSession || state == Working) {
311 QSharedPointer<QNetworkSession> session(manager->d_func()->networkSession);
314 errorStr = session->errorString();
316 errorStr = QCoreApplication::translate("QNetworkReply", "Network session error.");
317 error(QNetworkReplyImpl::NetworkSessionFailedError, errorStr);
323 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
326 Q_Q(QNetworkReplyImpl);
333 q->QIODevice::open(QIODevice::ReadOnly);
334 // Internal code that does a HTTP reply for the synchronous Ajax
336 QVariant synchronousHttpAttribute = req.attribute(
337 static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
338 // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
339 // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
340 if (synchronousHttpAttribute.toBool() && outgoingData) {
341 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
342 qint64 previousDataSize = 0;
344 previousDataSize = outgoingDataBuffer->size();
345 outgoingDataBuffer->append(outgoingData->readAll());
346 } while (outgoingDataBuffer->size() != previousDataSize);
350 backend->setSynchronous(synchronousHttpAttribute.toBool());
353 if (outgoingData && backend && !backend->isSynchronous()) {
354 // there is data to be uploaded, e.g. HTTP POST.
356 if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
357 // backend does not need upload buffering or
358 // fixed size non-sequential
359 // just start the operation
360 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
362 bool bufferingDisallowed =
363 req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
366 if (bufferingDisallowed) {
367 // if a valid content-length header for the request was supplied, we can disable buffering
368 // if not, we will buffer anyway
369 if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
370 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
373 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
376 // _q_startOperation will be called when the buffering has finished.
378 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
382 // for HTTP, we want to send out the request as fast as possible to the network, without
383 // invoking methods in a QueuedConnection
385 if (backend && backend->isSynchronous()) {
388 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
391 if (backend && backend->isSynchronous())
394 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
399 void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
401 Q_Q(QNetworkReplyImpl);
402 if (!pendingNotifications.contains(notification))
403 pendingNotifications.enqueue(notification);
405 if (pendingNotifications.size() == 1)
406 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
409 void QNetworkReplyImplPrivate::handleNotifications()
411 if (notificationHandlingPaused)
414 NotificationQueue current = pendingNotifications;
415 pendingNotifications.clear();
417 if (state != Working)
420 while (state == Working && !current.isEmpty()) {
421 InternalNotifications notification = current.dequeue();
422 switch (notification) {
423 case NotifyDownstreamReadyWrite:
427 backend->downstreamReadyWrite();
430 case NotifyCloseDownstreamChannel:
431 backend->closeDownstreamChannel();
434 case NotifyCopyFinished: {
435 QIODevice *dev = copyDevice;
437 backend->copyFinished(dev);
444 // Do not handle the notifications while we are emitting downloadProgress
446 void QNetworkReplyImplPrivate::pauseNotificationHandling()
448 notificationHandlingPaused = true;
451 // Resume notification handling
452 void QNetworkReplyImplPrivate::resumeNotificationHandling()
454 Q_Q(QNetworkReplyImpl);
455 notificationHandlingPaused = false;
456 if (pendingNotifications.size() >= 1)
457 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
460 QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
464 return backend->networkCache();
467 void QNetworkReplyImplPrivate::createCache()
469 // check if we can save and if we're allowed to
471 || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool())
476 bool QNetworkReplyImplPrivate::isCachingEnabled() const
478 return (cacheEnabled && networkCache() != 0);
481 void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
483 if (!enable && !cacheEnabled)
484 return; // nothing to do
485 if (enable && cacheEnabled)
486 return; // nothing to do either!
489 if (bytesDownloaded) {
490 // refuse to enable in this case
491 qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
497 // someone told us to turn on, then back off?
498 // ok... but you should make up your mind
499 qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
500 "backend %s probably needs to be fixed",
501 backend->metaObject()->className());
502 networkCache()->remove(url);
504 cacheEnabled = false;
508 void QNetworkReplyImplPrivate::completeCacheSave()
510 if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
511 networkCache()->remove(url);
512 } else if (cacheEnabled && cacheSaveDevice) {
513 networkCache()->insert(cacheSaveDevice);
516 cacheEnabled = false;
519 void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
521 Q_Q(QNetworkReplyImpl);
522 bytesUploaded = bytesSent;
523 pauseNotificationHandling();
524 emit q->uploadProgress(bytesSent, bytesTotal);
525 resumeNotificationHandling();
529 qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
531 enum { DesiredBufferSize = 32 * 1024 };
532 if (readBufferMaxSize == 0)
533 return DesiredBufferSize;
535 return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
538 void QNetworkReplyImplPrivate::initCacheSaveDevice()
540 Q_Q(QNetworkReplyImpl);
542 // The disk cache does not support partial content, so don't even try to
543 // save any such content into the cache.
544 if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
545 cacheEnabled = false;
549 // save the meta data
550 QNetworkCacheMetaData metaData;
551 metaData.setUrl(url);
552 metaData = backend->fetchCacheMetaData(metaData);
554 // save the redirect request also in the cache
555 QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
556 if (redirectionTarget.isValid()) {
557 QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
558 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
559 metaData.setAttributes(attributes);
562 cacheSaveDevice = networkCache()->prepare(metaData);
564 if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
565 if (cacheSaveDevice && !cacheSaveDevice->isOpen())
566 qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
567 "class %s probably needs to be fixed",
568 networkCache()->metaObject()->className());
570 networkCache()->remove(url);
572 cacheEnabled = false;
576 // we received downstream data and send this to the cache
577 // and to our readBuffer (which in turn gets read by the user of QNetworkReply)
578 void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
580 Q_Q(QNetworkReplyImpl);
584 if (cacheEnabled && !cacheSaveDevice) {
585 initCacheSaveDevice();
588 qint64 bytesWritten = 0;
589 for (int i = 0; i < data.bufferCount(); i++) {
590 QByteArray const &item = data[i];
593 cacheSaveDevice->write(item.constData(), item.size());
594 readBuffer.append(item);
596 bytesWritten += item.size();
600 bytesDownloaded += bytesWritten;
601 lastBytesDownloaded = bytesDownloaded;
603 appendDownstreamDataSignalEmissions();
606 void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
608 Q_Q(QNetworkReplyImpl);
610 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
611 if (preMigrationDownloaded != Q_INT64_C(-1))
612 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
613 pauseNotificationHandling();
614 // important: At the point of this readyRead(), the data parameter list must be empty,
615 // else implicit sharing will trigger memcpy when the user is reading data!
617 // emit readyRead before downloadProgress incase this will cause events to be
618 // processed and we get into a recursive call (as in QProgressDialog).
619 emit q->downloadProgress(bytesDownloaded,
620 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
622 resumeNotificationHandling();
623 // do we still have room in the buffer?
624 if (nextDownstreamBlockSize() > 0)
625 backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
628 // this is used when it was fetched from the cache, right?
629 void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
631 Q_Q(QNetworkReplyImpl);
635 // read until EOF from data
637 qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
638 "backend probly needs to be fixed");
643 q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
644 q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
650 void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
657 qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
660 static void downloadBufferDeleter(char *ptr)
665 char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
667 Q_Q(QNetworkReplyImpl);
669 if (!downloadBuffer) {
670 // We are requested to create it
671 // Check attribute() if allocating a buffer of that size can be allowed
672 QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
673 if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
674 downloadBufferCurrentSize = 0;
675 downloadBufferMaximumSize = size;
676 downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
677 downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
679 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
683 return downloadBuffer;
686 void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
688 Q_Q(QNetworkReplyImpl);
690 downloadBufferPointer = sp;
691 downloadBuffer = downloadBufferPointer.data();
692 downloadBufferCurrentSize = 0;
693 downloadBufferMaximumSize = size;
694 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
698 void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
700 Q_Q(QNetworkReplyImpl);
704 if (cacheEnabled && !cacheSaveDevice)
705 initCacheSaveDevice();
707 if (cacheSaveDevice && bytesReceived == bytesTotal) {
708 // if (lastBytesDownloaded == -1)
709 // lastBytesDownloaded = 0;
710 // cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
712 // Write everything in one go if we use a download buffer. might be more performant.
713 cacheSaveDevice->write(downloadBuffer, bytesTotal);
716 bytesDownloaded = bytesReceived;
717 lastBytesDownloaded = bytesReceived;
719 downloadBufferCurrentSize = bytesReceived;
721 // Only emit readyRead when actual data is there
722 // emit readyRead before downloadProgress incase this will cause events to be
723 // processed and we get into a recursive call (as in QProgressDialog).
724 if (bytesDownloaded > 0)
726 emit q->downloadProgress(bytesDownloaded, bytesTotal);
729 void QNetworkReplyImplPrivate::finished()
731 Q_Q(QNetworkReplyImpl);
733 if (state == Finished || state == Aborted || state == WaitingForSession)
736 pauseNotificationHandling();
737 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
738 if (preMigrationDownloaded != Q_INT64_C(-1))
739 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
741 if (!manager.isNull()) {
742 #ifndef QT_NO_BEARERMANAGEMENT
743 QNetworkSession *session = manager->d_func()->networkSession.data();
744 if (session && session->state() == QNetworkSession::Roaming &&
745 state == Working && errorCode != QNetworkReply::OperationCanceledError) {
746 // only content with a known size will fail with a temporary network failure error
747 if (!totalSize.isNull()) {
748 if (bytesDownloaded != totalSize) {
749 if (migrateBackend()) {
750 // either we are migrating or the request is finished/aborted
751 if (state == Reconnecting || state == WaitingForSession) {
752 resumeNotificationHandling();
753 return; // exit early if we are migrating.
756 error(QNetworkReply::TemporaryNetworkFailureError,
757 QNetworkReply::tr("Temporary network failure."));
764 resumeNotificationHandling();
767 q->setFinished(true);
769 pendingNotifications.clear();
771 pauseNotificationHandling();
772 if (totalSize.isNull() || totalSize == -1) {
773 emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
776 if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
777 emit q->uploadProgress(0, 0);
778 resumeNotificationHandling();
780 // if we don't know the total size of or we received everything save the cache
781 if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
784 // note: might not be a good idea, since users could decide to delete us
785 // which would delete the backend too...
786 // maybe we should protect the backend
787 pauseNotificationHandling();
788 emit q->readChannelFinished();
790 resumeNotificationHandling();
793 void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
795 Q_Q(QNetworkReplyImpl);
796 // Can't set and emit multiple errors.
797 if (errorCode != QNetworkReply::NoError) {
798 qWarning( "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
803 q->setErrorString(errorMessage);
805 // note: might not be a good idea, since users could decide to delete us
806 // which would delete the backend too...
807 // maybe we should protect the backend
811 void QNetworkReplyImplPrivate::metaDataChanged()
813 Q_Q(QNetworkReplyImpl);
814 // 1. do we have cookies?
815 // 2. are we allowed to set them?
816 if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()
817 && (static_cast<QNetworkRequest::LoadControl>
818 (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
819 QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
820 QList<QNetworkCookie> cookies =
821 qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
822 QNetworkCookieJar *jar = manager->cookieJar();
824 jar->setCookiesFromUrl(cookies, url);
826 emit q->metaDataChanged();
829 void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
831 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
834 void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
837 Q_Q(QNetworkReplyImpl);
838 emit q->sslErrors(errors);
844 QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
845 : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
849 QNetworkReplyImpl::~QNetworkReplyImpl()
851 Q_D(QNetworkReplyImpl);
853 // This code removes the data from the cache if it was prematurely aborted.
854 // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
855 // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
856 if (d->isCachingEnabled())
857 d->networkCache()->remove(url());
860 void QNetworkReplyImpl::abort()
862 Q_D(QNetworkReplyImpl);
863 if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
866 // stop both upload and download
868 disconnect(d->outgoingData, 0, this, 0);
870 disconnect(d->copyDevice, 0, this, 0);
872 QNetworkReply::close();
874 if (d->state != QNetworkReplyImplPrivate::Finished) {
875 // call finished which will emit signals
876 d->error(OperationCanceledError, tr("Operation canceled"));
877 if (d->state == QNetworkReplyImplPrivate::WaitingForSession)
878 d->state = QNetworkReplyImplPrivate::Working;
881 d->state = QNetworkReplyImplPrivate::Aborted;
883 // finished may access the backend
885 d->backend->deleteLater();
890 void QNetworkReplyImpl::close()
892 Q_D(QNetworkReplyImpl);
893 if (d->state == QNetworkReplyImplPrivate::Aborted ||
894 d->state == QNetworkReplyImplPrivate::Finished)
899 d->backend->closeDownstreamChannel();
901 disconnect(d->copyDevice, 0, this, 0);
903 QNetworkReply::close();
905 // call finished which will emit signals
906 d->error(OperationCanceledError, tr("Operation canceled"));
910 bool QNetworkReplyImpl::canReadLine () const
912 Q_D(const QNetworkReplyImpl);
913 return QNetworkReply::canReadLine() || d->readBuffer.canReadLine();
918 Returns the number of bytes available for reading with
919 QIODevice::read(). The number of bytes available may grow until
920 the finished() signal is emitted.
922 qint64 QNetworkReplyImpl::bytesAvailable() const
924 // Special case for the "zero copy" download buffer
925 Q_D(const QNetworkReplyImpl);
926 if (d->downloadBuffer) {
927 qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
928 return QNetworkReply::bytesAvailable() + maxAvail;
931 return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
934 void QNetworkReplyImpl::setReadBufferSize(qint64 size)
936 Q_D(QNetworkReplyImpl);
937 if (size > d->readBufferMaxSize &&
938 size > d->readBuffer.byteAmount())
939 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
941 QNetworkReply::setReadBufferSize(size);
944 d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
948 void QNetworkReplyImpl::sslConfigurationImplementation(QSslConfiguration &configuration) const
950 Q_D(const QNetworkReplyImpl);
952 d->backend->fetchSslConfiguration(configuration);
955 void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
957 Q_D(QNetworkReplyImpl);
958 if (d->backend && !config.isNull())
959 d->backend->setSslConfiguration(config);
962 void QNetworkReplyImpl::ignoreSslErrors()
964 Q_D(QNetworkReplyImpl);
966 d->backend->ignoreSslErrors();
969 void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
971 Q_D(QNetworkReplyImpl);
973 d->backend->ignoreSslErrors(errors);
980 qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
982 Q_D(QNetworkReplyImpl);
984 // Special case code if we have the "zero copy" download buffer
985 if (d->downloadBuffer) {
986 qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
988 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
989 // FIXME what about "Aborted" state?
990 memcpy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
991 d->downloadBufferReadPosition += maxAvail;
996 if (d->readBuffer.isEmpty())
997 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
998 // FIXME what about "Aborted" state?
1000 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
1002 // optimization for getChar()
1003 *data = d->readBuffer.getChar();
1007 maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
1008 return d->readBuffer.read(data, maxlen);
1012 \internal Reimplemented for internal purposes
1014 bool QNetworkReplyImpl::event(QEvent *e)
1016 if (e->type() == QEvent::NetworkReplyUpdated) {
1017 d_func()->handleNotifications();
1021 return QObject::event(e);
1025 Migrates the backend of the QNetworkReply to a new network connection if required. Returns
1026 true if the reply is migrated or it is not required; otherwise returns false.
1028 bool QNetworkReplyImplPrivate::migrateBackend()
1030 Q_Q(QNetworkReplyImpl);
1032 // Network reply is already finished or aborted, don't need to migrate.
1033 if (state == Finished || state == Aborted)
1036 // Request has outgoing data, not migrating.
1040 // Request is serviced from the cache, don't need to migrate.
1044 // Backend does not support resuming download.
1045 if (!backend->canResume())
1048 state = QNetworkReplyImplPrivate::Reconnecting;
1055 cookedHeaders.clear();
1058 preMigrationDownloaded = bytesDownloaded;
1060 backend = manager->d_func()->findBackend(operation, request);
1063 backend->setParent(q);
1064 backend->reply = this;
1065 backend->setResumeOffset(bytesDownloaded);
1069 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1071 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1072 #endif // QT_NO_HTTP
1077 #ifndef QT_NO_BEARERMANAGEMENT
1078 QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent,
1079 const QNetworkRequest &req,
1080 QNetworkAccessManager::Operation op)
1081 : QNetworkReply(parent)
1087 qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
1089 QString msg = QCoreApplication::translate("QNetworkAccessManager",
1090 "Network access is disabled.");
1091 setError(UnknownNetworkError, msg);
1093 QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
1094 Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
1095 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
1098 QDisabledNetworkReply::~QDisabledNetworkReply()
1105 #include "moc_qnetworkreplyimpl_p.cpp"