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 if (!backend->start()) {
94 #ifndef QT_NO_BEARERMANAGEMENT
95 // backend failed to start because the session state is not Connected.
96 // QNetworkAccessManager will call _q_startOperation again for us when the session
98 state = WaitingForSession;
100 QNetworkSession *session = manager->d_func()->networkSession.data();
103 Q_Q(QNetworkReplyImpl);
105 QObject::connect(session, SIGNAL(error(QNetworkSession::SessionError)),
106 q, SLOT(_q_networkSessionFailed()));
108 if (!session->isOpen()) {
109 session->setSessionProperty(QStringLiteral("ConnectInBackground"),
110 backend->request().attribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(false)));
114 qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
116 error(QNetworkReplyImpl::NetworkSessionFailedError,
117 QCoreApplication::translate("QNetworkReply", "Network session error."));
121 qWarning("Backend start failed");
123 error(QNetworkReplyImpl::UnknownNetworkError,
124 QCoreApplication::translate("QNetworkReply", "backend start error."));
130 if (backend && backend->isSynchronous()) {
132 q_func()->setFinished(true);
134 if (state != Finished) {
135 if (operation == QNetworkAccessManager::GetOperation)
136 pendingNotifications.append(NotifyDownstreamReadyWrite);
138 handleNotifications();
143 void QNetworkReplyImplPrivate::_q_copyReadyRead()
145 Q_Q(QNetworkReplyImpl);
146 if (state != Working)
148 if (!copyDevice || !q->isOpen())
151 // FIXME Optimize to use download buffer if it is a QBuffer.
152 // Needs to be done where sendCacheContents() (?) of HTTP is emitting
156 qint64 bytesToRead = nextDownstreamBlockSize();
157 if (bytesToRead == 0)
158 // we'll be called again, eventually
161 bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
163 byteData.resize(bytesToRead);
164 qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
165 if (bytesActuallyRead == -1) {
167 backendNotify(NotifyCopyFinished);
171 byteData.resize(bytesActuallyRead);
172 readBuffer.append(byteData);
174 if (!copyDevice->isSequential() && copyDevice->atEnd()) {
175 backendNotify(NotifyCopyFinished);
176 bytesDownloaded += bytesActuallyRead;
180 bytesDownloaded += bytesActuallyRead;
183 if (bytesDownloaded == lastBytesDownloaded) {
184 // we didn't read anything
188 lastBytesDownloaded = bytesDownloaded;
189 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
190 if (preMigrationDownloaded != Q_INT64_C(-1))
191 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
192 pauseNotificationHandling();
193 // emit readyRead before downloadProgress incase this will cause events to be
194 // processed and we get into a recursive call (as in QProgressDialog).
196 emit q->downloadProgress(bytesDownloaded,
197 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
198 resumeNotificationHandling();
201 void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
206 void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
208 Q_Q(QNetworkReplyImpl);
210 // make sure this is only called once, ever.
211 //_q_bufferOutgoingData may call it or the readChannelFinished emission
212 if (state != Buffering)
215 // disconnect signals
216 QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
217 QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
219 // finally, start the request
220 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
223 void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
225 Q_Q(QNetworkReplyImpl);
227 if (!outgoingDataBuffer) {
228 // first call, create our buffer
229 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
231 QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
232 QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
235 qint64 bytesBuffered = 0;
236 qint64 bytesToBuffer = 0;
238 // read data into our buffer
240 bytesToBuffer = outgoingData->bytesAvailable();
241 // unknown? just try 2 kB, this also ensures we always try to read the EOF
242 if (bytesToBuffer <= 0)
243 bytesToBuffer = 2*1024;
245 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
246 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
248 if (bytesBuffered == -1) {
249 // EOF has been reached.
250 outgoingDataBuffer->chop(bytesToBuffer);
252 _q_bufferOutgoingDataFinished();
254 } else if (bytesBuffered == 0) {
255 // nothing read right now, just wait until we get called again
256 outgoingDataBuffer->chop(bytesToBuffer);
260 // don't break, try to read() again
261 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
266 #ifndef QT_NO_BEARERMANAGEMENT
267 void QNetworkReplyImplPrivate::_q_networkSessionConnected()
269 Q_Q(QNetworkReplyImpl);
271 if (manager.isNull())
274 QNetworkSession *session = manager->d_func()->networkSession.data();
278 if (session->state() != QNetworkSession::Connected)
282 case QNetworkReplyImplPrivate::Buffering:
283 case QNetworkReplyImplPrivate::Working:
284 case QNetworkReplyImplPrivate::Reconnecting:
285 // Migrate existing downloads to new network connection.
288 case QNetworkReplyImplPrivate::WaitingForSession:
289 // Start waiting requests.
290 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
297 void QNetworkReplyImplPrivate::_q_networkSessionFailed()
299 // Abort waiting and working replies.
300 if (state == WaitingForSession || state == Working) {
302 QSharedPointer<QNetworkSession> session(manager->d_func()->networkSession);
305 errorStr = session->errorString();
307 errorStr = QCoreApplication::translate("QNetworkReply", "Network session error.");
308 error(QNetworkReplyImpl::NetworkSessionFailedError, errorStr);
314 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
317 Q_Q(QNetworkReplyImpl);
324 q->QIODevice::open(QIODevice::ReadOnly);
325 // Internal code that does a HTTP reply for the synchronous Ajax
327 QVariant synchronousHttpAttribute = req.attribute(
328 static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
329 // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
330 // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
331 if (synchronousHttpAttribute.toBool() && outgoingData) {
332 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
333 qint64 previousDataSize = 0;
335 previousDataSize = outgoingDataBuffer->size();
336 outgoingDataBuffer->append(outgoingData->readAll());
337 } while (outgoingDataBuffer->size() != previousDataSize);
341 backend->setSynchronous(synchronousHttpAttribute.toBool());
344 if (outgoingData && backend && !backend->isSynchronous()) {
345 // there is data to be uploaded, e.g. HTTP POST.
347 if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
348 // backend does not need upload buffering or
349 // fixed size non-sequential
350 // just start the operation
351 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
353 bool bufferingDisallowed =
354 req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
357 if (bufferingDisallowed) {
358 // if a valid content-length header for the request was supplied, we can disable buffering
359 // if not, we will buffer anyway
360 if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
361 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
364 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
367 // _q_startOperation will be called when the buffering has finished.
369 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
373 // for HTTP, we want to send out the request as fast as possible to the network, without
374 // invoking methods in a QueuedConnection
376 if (backend && backend->isSynchronous()) {
379 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
382 if (backend && backend->isSynchronous())
385 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
390 void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
392 Q_Q(QNetworkReplyImpl);
393 if (!pendingNotifications.contains(notification))
394 pendingNotifications.enqueue(notification);
396 if (pendingNotifications.size() == 1)
397 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
400 void QNetworkReplyImplPrivate::handleNotifications()
402 if (notificationHandlingPaused)
405 NotificationQueue current = pendingNotifications;
406 pendingNotifications.clear();
408 if (state != Working)
411 while (state == Working && !current.isEmpty()) {
412 InternalNotifications notification = current.dequeue();
413 switch (notification) {
414 case NotifyDownstreamReadyWrite:
418 backend->downstreamReadyWrite();
421 case NotifyCloseDownstreamChannel:
422 backend->closeDownstreamChannel();
425 case NotifyCopyFinished: {
426 QIODevice *dev = copyDevice;
428 backend->copyFinished(dev);
435 // Do not handle the notifications while we are emitting downloadProgress
437 void QNetworkReplyImplPrivate::pauseNotificationHandling()
439 notificationHandlingPaused = true;
442 // Resume notification handling
443 void QNetworkReplyImplPrivate::resumeNotificationHandling()
445 Q_Q(QNetworkReplyImpl);
446 notificationHandlingPaused = false;
447 if (pendingNotifications.size() >= 1)
448 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
451 QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
455 return backend->networkCache();
458 void QNetworkReplyImplPrivate::createCache()
460 // check if we can save and if we're allowed to
462 || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()
463 || request.attribute(QNetworkRequest::CacheLoadControlAttribute,
464 QNetworkRequest::PreferNetwork).toInt()
465 == QNetworkRequest::AlwaysNetwork)
470 bool QNetworkReplyImplPrivate::isCachingEnabled() const
472 return (cacheEnabled && networkCache() != 0);
475 void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
477 if (!enable && !cacheEnabled)
478 return; // nothing to do
479 if (enable && cacheEnabled)
480 return; // nothing to do either!
483 if (bytesDownloaded) {
484 // refuse to enable in this case
485 qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
491 // someone told us to turn on, then back off?
492 // ok... but you should make up your mind
493 qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
494 "backend %s probably needs to be fixed",
495 backend->metaObject()->className());
496 networkCache()->remove(url);
498 cacheEnabled = false;
502 void QNetworkReplyImplPrivate::completeCacheSave()
504 if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
505 networkCache()->remove(url);
506 } else if (cacheEnabled && cacheSaveDevice) {
507 networkCache()->insert(cacheSaveDevice);
510 cacheEnabled = false;
513 void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
515 Q_Q(QNetworkReplyImpl);
516 bytesUploaded = bytesSent;
517 pauseNotificationHandling();
518 emit q->uploadProgress(bytesSent, bytesTotal);
519 resumeNotificationHandling();
523 qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
525 enum { DesiredBufferSize = 32 * 1024 };
526 if (readBufferMaxSize == 0)
527 return DesiredBufferSize;
529 return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
532 void QNetworkReplyImplPrivate::initCacheSaveDevice()
534 Q_Q(QNetworkReplyImpl);
536 // The disk cache does not support partial content, so don't even try to
537 // save any such content into the cache.
538 if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
539 cacheEnabled = false;
543 // save the meta data
544 QNetworkCacheMetaData metaData;
545 metaData.setUrl(url);
546 metaData = backend->fetchCacheMetaData(metaData);
548 // save the redirect request also in the cache
549 QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
550 if (redirectionTarget.isValid()) {
551 QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
552 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
553 metaData.setAttributes(attributes);
556 cacheSaveDevice = networkCache()->prepare(metaData);
558 if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
559 if (cacheSaveDevice && !cacheSaveDevice->isOpen())
560 qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
561 "class %s probably needs to be fixed",
562 networkCache()->metaObject()->className());
564 networkCache()->remove(url);
566 cacheEnabled = false;
570 // we received downstream data and send this to the cache
571 // and to our readBuffer (which in turn gets read by the user of QNetworkReply)
572 void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
574 Q_Q(QNetworkReplyImpl);
578 if (cacheEnabled && !cacheSaveDevice) {
579 initCacheSaveDevice();
582 qint64 bytesWritten = 0;
583 for (int i = 0; i < data.bufferCount(); i++) {
584 QByteArray const &item = data[i];
587 cacheSaveDevice->write(item.constData(), item.size());
588 readBuffer.append(item);
590 bytesWritten += item.size();
594 bytesDownloaded += bytesWritten;
595 lastBytesDownloaded = bytesDownloaded;
597 appendDownstreamDataSignalEmissions();
600 void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
602 Q_Q(QNetworkReplyImpl);
604 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
605 if (preMigrationDownloaded != Q_INT64_C(-1))
606 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
607 pauseNotificationHandling();
608 // important: At the point of this readyRead(), the data parameter list must be empty,
609 // else implicit sharing will trigger memcpy when the user is reading data!
611 // emit readyRead before downloadProgress incase this will cause events to be
612 // processed and we get into a recursive call (as in QProgressDialog).
613 emit q->downloadProgress(bytesDownloaded,
614 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
616 resumeNotificationHandling();
617 // do we still have room in the buffer?
618 if (nextDownstreamBlockSize() > 0)
619 backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
622 // this is used when it was fetched from the cache, right?
623 void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
625 Q_Q(QNetworkReplyImpl);
629 // read until EOF from data
631 qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
632 "backend probly needs to be fixed");
637 q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
638 q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
644 void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
651 qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
654 static void downloadBufferDeleter(char *ptr)
659 char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
661 Q_Q(QNetworkReplyImpl);
663 if (!downloadBuffer) {
664 // We are requested to create it
665 // Check attribute() if allocating a buffer of that size can be allowed
666 QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
667 if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
668 downloadBufferCurrentSize = 0;
669 downloadBufferMaximumSize = size;
670 downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
671 downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
673 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
677 return downloadBuffer;
680 void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
682 Q_Q(QNetworkReplyImpl);
684 downloadBufferPointer = sp;
685 downloadBuffer = downloadBufferPointer.data();
686 downloadBufferCurrentSize = 0;
687 downloadBufferMaximumSize = size;
688 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
692 void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
694 Q_Q(QNetworkReplyImpl);
698 if (cacheEnabled && !cacheSaveDevice)
699 initCacheSaveDevice();
701 if (cacheSaveDevice && bytesReceived == bytesTotal) {
702 // if (lastBytesDownloaded == -1)
703 // lastBytesDownloaded = 0;
704 // cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
706 // Write everything in one go if we use a download buffer. might be more performant.
707 cacheSaveDevice->write(downloadBuffer, bytesTotal);
710 bytesDownloaded = bytesReceived;
711 lastBytesDownloaded = bytesReceived;
713 downloadBufferCurrentSize = bytesReceived;
715 // Only emit readyRead when actual data is there
716 // emit readyRead before downloadProgress incase this will cause events to be
717 // processed and we get into a recursive call (as in QProgressDialog).
718 if (bytesDownloaded > 0)
720 emit q->downloadProgress(bytesDownloaded, bytesTotal);
723 void QNetworkReplyImplPrivate::finished()
725 Q_Q(QNetworkReplyImpl);
727 if (state == Finished || state == Aborted || state == WaitingForSession)
730 pauseNotificationHandling();
731 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
732 if (preMigrationDownloaded != Q_INT64_C(-1))
733 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
735 if (!manager.isNull()) {
736 #ifndef QT_NO_BEARERMANAGEMENT
737 QNetworkSession *session = manager->d_func()->networkSession.data();
738 if (session && session->state() == QNetworkSession::Roaming &&
739 state == Working && errorCode != QNetworkReply::OperationCanceledError) {
740 // only content with a known size will fail with a temporary network failure error
741 if (!totalSize.isNull()) {
742 if (bytesDownloaded != totalSize) {
743 if (migrateBackend()) {
744 // either we are migrating or the request is finished/aborted
745 if (state == Reconnecting || state == WaitingForSession) {
746 resumeNotificationHandling();
747 return; // exit early if we are migrating.
750 error(QNetworkReply::TemporaryNetworkFailureError,
751 QNetworkReply::tr("Temporary network failure."));
758 resumeNotificationHandling();
761 q->setFinished(true);
763 pendingNotifications.clear();
765 pauseNotificationHandling();
766 if (totalSize.isNull() || totalSize == -1) {
767 emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
770 if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
771 emit q->uploadProgress(0, 0);
772 resumeNotificationHandling();
774 // if we don't know the total size of or we received everything save the cache
775 if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
778 // note: might not be a good idea, since users could decide to delete us
779 // which would delete the backend too...
780 // maybe we should protect the backend
781 pauseNotificationHandling();
782 emit q->readChannelFinished();
784 resumeNotificationHandling();
787 void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
789 Q_Q(QNetworkReplyImpl);
790 // Can't set and emit multiple errors.
791 if (errorCode != QNetworkReply::NoError) {
792 qWarning( "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
797 q->setErrorString(errorMessage);
799 // note: might not be a good idea, since users could decide to delete us
800 // which would delete the backend too...
801 // maybe we should protect the backend
805 void QNetworkReplyImplPrivate::metaDataChanged()
807 Q_Q(QNetworkReplyImpl);
808 // 1. do we have cookies?
809 // 2. are we allowed to set them?
810 if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()
811 && (static_cast<QNetworkRequest::LoadControl>
812 (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
813 QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
814 QList<QNetworkCookie> cookies =
815 qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
816 QNetworkCookieJar *jar = manager->cookieJar();
818 jar->setCookiesFromUrl(cookies, url);
820 emit q->metaDataChanged();
823 void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
825 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
828 void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
831 Q_Q(QNetworkReplyImpl);
832 emit q->sslErrors(errors);
838 QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
839 : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
843 QNetworkReplyImpl::~QNetworkReplyImpl()
845 Q_D(QNetworkReplyImpl);
847 // This code removes the data from the cache if it was prematurely aborted.
848 // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
849 // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
850 if (d->isCachingEnabled())
851 d->networkCache()->remove(url());
854 void QNetworkReplyImpl::abort()
856 Q_D(QNetworkReplyImpl);
857 if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
860 // stop both upload and download
862 disconnect(d->outgoingData, 0, this, 0);
864 disconnect(d->copyDevice, 0, this, 0);
866 QNetworkReply::close();
868 if (d->state != QNetworkReplyImplPrivate::Finished) {
869 // call finished which will emit signals
870 d->error(OperationCanceledError, tr("Operation canceled"));
871 if (d->state == QNetworkReplyImplPrivate::WaitingForSession)
872 d->state = QNetworkReplyImplPrivate::Working;
875 d->state = QNetworkReplyImplPrivate::Aborted;
877 // finished may access the backend
879 d->backend->deleteLater();
884 void QNetworkReplyImpl::close()
886 Q_D(QNetworkReplyImpl);
887 if (d->state == QNetworkReplyImplPrivate::Aborted ||
888 d->state == QNetworkReplyImplPrivate::Finished)
893 d->backend->closeDownstreamChannel();
895 disconnect(d->copyDevice, 0, this, 0);
897 QNetworkReply::close();
899 // call finished which will emit signals
900 d->error(OperationCanceledError, tr("Operation canceled"));
904 bool QNetworkReplyImpl::canReadLine () const
906 Q_D(const QNetworkReplyImpl);
907 return QNetworkReply::canReadLine() || d->readBuffer.canReadLine();
912 Returns the number of bytes available for reading with
913 QIODevice::read(). The number of bytes available may grow until
914 the finished() signal is emitted.
916 qint64 QNetworkReplyImpl::bytesAvailable() const
918 // Special case for the "zero copy" download buffer
919 Q_D(const QNetworkReplyImpl);
920 if (d->downloadBuffer) {
921 qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
922 return QNetworkReply::bytesAvailable() + maxAvail;
925 return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
928 void QNetworkReplyImpl::setReadBufferSize(qint64 size)
930 Q_D(QNetworkReplyImpl);
931 if (size > d->readBufferMaxSize &&
932 size > d->readBuffer.byteAmount())
933 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
935 QNetworkReply::setReadBufferSize(size);
938 d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
942 void QNetworkReplyImpl::sslConfigurationImplementation(QSslConfiguration &configuration) const
944 Q_D(const QNetworkReplyImpl);
946 d->backend->fetchSslConfiguration(configuration);
949 void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
951 Q_D(QNetworkReplyImpl);
952 if (d->backend && !config.isNull())
953 d->backend->setSslConfiguration(config);
956 void QNetworkReplyImpl::ignoreSslErrors()
958 Q_D(QNetworkReplyImpl);
960 d->backend->ignoreSslErrors();
963 void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
965 Q_D(QNetworkReplyImpl);
967 d->backend->ignoreSslErrors(errors);
974 qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
976 Q_D(QNetworkReplyImpl);
978 // Special case code if we have the "zero copy" download buffer
979 if (d->downloadBuffer) {
980 qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
982 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
983 // FIXME what about "Aborted" state?
984 memcpy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
985 d->downloadBufferReadPosition += maxAvail;
990 if (d->readBuffer.isEmpty())
991 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
992 // FIXME what about "Aborted" state?
994 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
996 // optimization for getChar()
997 *data = d->readBuffer.getChar();
1001 maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
1002 return d->readBuffer.read(data, maxlen);
1006 \internal Reimplemented for internal purposes
1008 bool QNetworkReplyImpl::event(QEvent *e)
1010 if (e->type() == QEvent::NetworkReplyUpdated) {
1011 d_func()->handleNotifications();
1015 return QObject::event(e);
1019 Migrates the backend of the QNetworkReply to a new network connection if required. Returns
1020 true if the reply is migrated or it is not required; otherwise returns false.
1022 bool QNetworkReplyImplPrivate::migrateBackend()
1024 Q_Q(QNetworkReplyImpl);
1026 // Network reply is already finished or aborted, don't need to migrate.
1027 if (state == Finished || state == Aborted)
1030 // Request has outgoing data, not migrating.
1034 // Request is serviced from the cache, don't need to migrate.
1038 // Backend does not support resuming download.
1039 if (!backend->canResume())
1042 state = QNetworkReplyImplPrivate::Reconnecting;
1049 cookedHeaders.clear();
1052 preMigrationDownloaded = bytesDownloaded;
1054 backend = manager->d_func()->findBackend(operation, request);
1057 backend->setParent(q);
1058 backend->reply = this;
1059 backend->setResumeOffset(bytesDownloaded);
1063 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1065 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1066 #endif // QT_NO_HTTP
1071 #ifndef QT_NO_BEARERMANAGEMENT
1072 QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent,
1073 const QNetworkRequest &req,
1074 QNetworkAccessManager::Operation op)
1075 : QNetworkReply(parent)
1081 qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
1083 QString msg = QCoreApplication::translate("QNetworkAccessManager",
1084 "Network access is disabled.");
1085 setError(UnknownNetworkError, msg);
1087 QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
1088 Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
1089 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
1092 QDisabledNetworkReply::~QDisabledNetworkReply()
1099 #include "moc_qnetworkreplyimpl_p.cpp"