1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtNetwork module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qnetworkreplyimpl_p.h"
43 #include "qnetworkaccessbackend_p.h"
44 #include "qnetworkcookie.h"
45 #include "qabstractnetworkcache.h"
46 #include "QtCore/qcoreapplication.h"
47 #include "QtCore/qdatetime.h"
48 #include "QtNetwork/qsslconfiguration.h"
49 #include "QtNetwork/qnetworksession.h"
50 #include "qnetworkaccesshttpbackend_p.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 if (!backend->start()) { // ### we should call that method even if bearer is not used
95 // backend failed to start because the session state is not Connected.
96 // QNetworkAccessManager will call reply->backend->start() 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())
111 qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
118 if (backend && backend->isSynchronous()) {
120 q_func()->setFinished(true);
122 if (state != Finished) {
123 if (operation == QNetworkAccessManager::GetOperation)
124 pendingNotifications.append(NotifyDownstreamReadyWrite);
126 handleNotifications();
131 void QNetworkReplyImplPrivate::_q_copyReadyRead()
133 Q_Q(QNetworkReplyImpl);
134 if (state != Working)
136 if (!copyDevice || !q->isOpen())
139 // FIXME Optimize to use download buffer if it is a QBuffer.
140 // Needs to be done where sendCacheContents() (?) of HTTP is emitting
144 qint64 bytesToRead = nextDownstreamBlockSize();
145 if (bytesToRead == 0)
146 // we'll be called again, eventually
149 bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
151 byteData.resize(bytesToRead);
152 qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
153 if (bytesActuallyRead == -1) {
155 backendNotify(NotifyCopyFinished);
159 byteData.resize(bytesActuallyRead);
160 readBuffer.append(byteData);
162 if (!copyDevice->isSequential() && copyDevice->atEnd()) {
163 backendNotify(NotifyCopyFinished);
164 bytesDownloaded += bytesActuallyRead;
168 bytesDownloaded += bytesActuallyRead;
171 if (bytesDownloaded == lastBytesDownloaded) {
172 // we didn't read anything
176 lastBytesDownloaded = bytesDownloaded;
177 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
178 if (preMigrationDownloaded != Q_INT64_C(-1))
179 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
180 pauseNotificationHandling();
181 // emit readyRead before downloadProgress incase this will cause events to be
182 // processed and we get into a recursive call (as in QProgressDialog).
184 emit q->downloadProgress(bytesDownloaded,
185 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
186 resumeNotificationHandling();
189 void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
194 void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
196 Q_Q(QNetworkReplyImpl);
198 // make sure this is only called once, ever.
199 //_q_bufferOutgoingData may call it or the readChannelFinished emission
200 if (state != Buffering)
203 // disconnect signals
204 QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
205 QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
207 // finally, start the request
208 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
211 void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
213 Q_Q(QNetworkReplyImpl);
215 if (!outgoingDataBuffer) {
216 // first call, create our buffer
217 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
219 QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
220 QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
223 qint64 bytesBuffered = 0;
224 qint64 bytesToBuffer = 0;
226 // read data into our buffer
228 bytesToBuffer = outgoingData->bytesAvailable();
229 // unknown? just try 2 kB, this also ensures we always try to read the EOF
230 if (bytesToBuffer <= 0)
231 bytesToBuffer = 2*1024;
233 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
234 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
236 if (bytesBuffered == -1) {
237 // EOF has been reached.
238 outgoingDataBuffer->chop(bytesToBuffer);
240 _q_bufferOutgoingDataFinished();
242 } else if (bytesBuffered == 0) {
243 // nothing read right now, just wait until we get called again
244 outgoingDataBuffer->chop(bytesToBuffer);
248 // don't break, try to read() again
249 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
254 #ifndef QT_NO_BEARERMANAGEMENT
255 void QNetworkReplyImplPrivate::_q_networkSessionConnected()
257 Q_Q(QNetworkReplyImpl);
259 if (manager.isNull())
262 QNetworkSession *session = manager->d_func()->networkSession.data();
266 if (session->state() != QNetworkSession::Connected)
270 case QNetworkReplyImplPrivate::Buffering:
271 case QNetworkReplyImplPrivate::Working:
272 case QNetworkReplyImplPrivate::Reconnecting:
273 // Migrate existing downloads to new network connection.
276 case QNetworkReplyImplPrivate::WaitingForSession:
277 // Start waiting requests.
278 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
285 void QNetworkReplyImplPrivate::_q_networkSessionFailed()
287 // Abort waiting and working replies.
288 if (state == WaitingForSession || state == Working) {
290 error(QNetworkReplyImpl::UnknownNetworkError,
291 QCoreApplication::translate("QNetworkReply", "Network session error."));
297 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
300 Q_Q(QNetworkReplyImpl);
307 q->QIODevice::open(QIODevice::ReadOnly);
308 // Internal code that does a HTTP reply for the synchronous Ajax
310 QVariant synchronousHttpAttribute = req.attribute(
311 static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
312 // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
313 // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
314 if (synchronousHttpAttribute.toBool() && outgoingData) {
315 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
316 qint64 previousDataSize = 0;
318 previousDataSize = outgoingDataBuffer->size();
319 outgoingDataBuffer->append(outgoingData->readAll());
320 } while (outgoingDataBuffer->size() != previousDataSize);
324 backend->setSynchronous(synchronousHttpAttribute.toBool());
327 if (outgoingData && backend && !backend->isSynchronous()) {
328 // there is data to be uploaded, e.g. HTTP POST.
330 if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
331 // backend does not need upload buffering or
332 // fixed size non-sequential
333 // just start the operation
334 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
336 bool bufferingDisallowed =
337 req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
340 if (bufferingDisallowed) {
341 // if a valid content-length header for the request was supplied, we can disable buffering
342 // if not, we will buffer anyway
343 if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
344 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
347 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
350 // _q_startOperation will be called when the buffering has finished.
352 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
356 // for HTTP, we want to send out the request as fast as possible to the network, without
357 // invoking methods in a QueuedConnection
359 if (qobject_cast<QNetworkAccessHttpBackend *>(backend) || (backend && backend->isSynchronous())) {
362 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
365 if (backend && backend->isSynchronous())
368 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
373 void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
375 Q_Q(QNetworkReplyImpl);
376 if (!pendingNotifications.contains(notification))
377 pendingNotifications.enqueue(notification);
379 if (pendingNotifications.size() == 1)
380 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
383 void QNetworkReplyImplPrivate::handleNotifications()
385 if (notificationHandlingPaused)
388 NotificationQueue current = pendingNotifications;
389 pendingNotifications.clear();
391 if (state != Working)
394 while (state == Working && !current.isEmpty()) {
395 InternalNotifications notification = current.dequeue();
396 switch (notification) {
397 case NotifyDownstreamReadyWrite:
401 backend->downstreamReadyWrite();
404 case NotifyCloseDownstreamChannel:
405 backend->closeDownstreamChannel();
408 case NotifyCopyFinished: {
409 QIODevice *dev = copyDevice;
411 backend->copyFinished(dev);
418 // Do not handle the notifications while we are emitting downloadProgress
420 void QNetworkReplyImplPrivate::pauseNotificationHandling()
422 notificationHandlingPaused = true;
425 // Resume notification handling
426 void QNetworkReplyImplPrivate::resumeNotificationHandling()
428 Q_Q(QNetworkReplyImpl);
429 notificationHandlingPaused = false;
430 if (pendingNotifications.size() >= 1)
431 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
434 QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
438 return backend->networkCache();
441 void QNetworkReplyImplPrivate::createCache()
443 // check if we can save and if we're allowed to
445 || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()
446 || request.attribute(QNetworkRequest::CacheLoadControlAttribute,
447 QNetworkRequest::PreferNetwork).toInt()
448 == QNetworkRequest::AlwaysNetwork)
453 bool QNetworkReplyImplPrivate::isCachingEnabled() const
455 return (cacheEnabled && networkCache() != 0);
458 void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
460 if (!enable && !cacheEnabled)
461 return; // nothing to do
462 if (enable && cacheEnabled)
463 return; // nothing to do either!
466 if (bytesDownloaded) {
467 // refuse to enable in this case
468 qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
474 // someone told us to turn on, then back off?
475 // ok... but you should make up your mind
476 qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
477 "backend %s probably needs to be fixed",
478 backend->metaObject()->className());
479 networkCache()->remove(url);
481 cacheEnabled = false;
485 void QNetworkReplyImplPrivate::completeCacheSave()
487 if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
488 networkCache()->remove(url);
489 } else if (cacheEnabled && cacheSaveDevice) {
490 networkCache()->insert(cacheSaveDevice);
493 cacheEnabled = false;
496 void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
498 Q_Q(QNetworkReplyImpl);
499 bytesUploaded = bytesSent;
500 pauseNotificationHandling();
501 emit q->uploadProgress(bytesSent, bytesTotal);
502 resumeNotificationHandling();
506 qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
508 enum { DesiredBufferSize = 32 * 1024 };
509 if (readBufferMaxSize == 0)
510 return DesiredBufferSize;
512 return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
515 void QNetworkReplyImplPrivate::initCacheSaveDevice()
517 Q_Q(QNetworkReplyImpl);
519 // The disk cache does not support partial content, so don't even try to
520 // save any such content into the cache.
521 if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
522 cacheEnabled = false;
526 // save the meta data
527 QNetworkCacheMetaData metaData;
528 metaData.setUrl(url);
529 metaData = backend->fetchCacheMetaData(metaData);
531 // save the redirect request also in the cache
532 QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
533 if (redirectionTarget.isValid()) {
534 QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
535 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
536 metaData.setAttributes(attributes);
539 cacheSaveDevice = networkCache()->prepare(metaData);
541 if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
542 if (cacheSaveDevice && !cacheSaveDevice->isOpen())
543 qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
544 "class %s probably needs to be fixed",
545 networkCache()->metaObject()->className());
547 networkCache()->remove(url);
549 cacheEnabled = false;
553 // we received downstream data and send this to the cache
554 // and to our readBuffer (which in turn gets read by the user of QNetworkReply)
555 void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
557 Q_Q(QNetworkReplyImpl);
561 if (cacheEnabled && !cacheSaveDevice) {
562 initCacheSaveDevice();
565 qint64 bytesWritten = 0;
566 for (int i = 0; i < data.bufferCount(); i++) {
567 QByteArray const &item = data[i];
570 cacheSaveDevice->write(item.constData(), item.size());
571 readBuffer.append(item);
573 bytesWritten += item.size();
577 bytesDownloaded += bytesWritten;
578 lastBytesDownloaded = bytesDownloaded;
580 appendDownstreamDataSignalEmissions();
583 void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
585 Q_Q(QNetworkReplyImpl);
587 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
588 if (preMigrationDownloaded != Q_INT64_C(-1))
589 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
590 pauseNotificationHandling();
591 // important: At the point of this readyRead(), the data parameter list must be empty,
592 // else implicit sharing will trigger memcpy when the user is reading data!
594 // emit readyRead before downloadProgress incase this will cause events to be
595 // processed and we get into a recursive call (as in QProgressDialog).
596 emit q->downloadProgress(bytesDownloaded,
597 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
599 resumeNotificationHandling();
600 // do we still have room in the buffer?
601 if (nextDownstreamBlockSize() > 0)
602 backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
605 // this is used when it was fetched from the cache, right?
606 void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
608 Q_Q(QNetworkReplyImpl);
612 // read until EOF from data
614 qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
615 "backend probly needs to be fixed");
620 q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
621 q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
627 void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
634 qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
637 static void downloadBufferDeleter(char *ptr)
642 char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
644 Q_Q(QNetworkReplyImpl);
646 if (!downloadBuffer) {
647 // We are requested to create it
648 // Check attribute() if allocating a buffer of that size can be allowed
649 QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
650 if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
651 downloadBufferCurrentSize = 0;
652 downloadBufferMaximumSize = size;
653 downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
654 downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
656 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
660 return downloadBuffer;
663 void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
665 Q_Q(QNetworkReplyImpl);
667 downloadBufferPointer = sp;
668 downloadBuffer = downloadBufferPointer.data();
669 downloadBufferCurrentSize = 0;
670 downloadBufferMaximumSize = size;
671 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
675 void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
677 Q_Q(QNetworkReplyImpl);
681 if (cacheEnabled && !cacheSaveDevice)
682 initCacheSaveDevice();
684 if (cacheSaveDevice && bytesReceived == bytesTotal) {
685 // if (lastBytesDownloaded == -1)
686 // lastBytesDownloaded = 0;
687 // cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
689 // Write everything in one go if we use a download buffer. might be more performant.
690 cacheSaveDevice->write(downloadBuffer, bytesTotal);
693 bytesDownloaded = bytesReceived;
694 lastBytesDownloaded = bytesReceived;
696 downloadBufferCurrentSize = bytesReceived;
698 // Only emit readyRead when actual data is there
699 // emit readyRead before downloadProgress incase this will cause events to be
700 // processed and we get into a recursive call (as in QProgressDialog).
701 if (bytesDownloaded > 0)
703 emit q->downloadProgress(bytesDownloaded, bytesTotal);
706 void QNetworkReplyImplPrivate::finished()
708 Q_Q(QNetworkReplyImpl);
710 if (state == Finished || state == Aborted || state == WaitingForSession)
713 pauseNotificationHandling();
714 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
715 if (preMigrationDownloaded != Q_INT64_C(-1))
716 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
718 if (!manager.isNull()) {
719 #ifndef QT_NO_BEARERMANAGEMENT
720 QNetworkSession *session = manager->d_func()->networkSession.data();
721 if (session && session->state() == QNetworkSession::Roaming &&
722 state == Working && errorCode != QNetworkReply::OperationCanceledError) {
723 // only content with a known size will fail with a temporary network failure error
724 if (!totalSize.isNull()) {
725 if (bytesDownloaded != totalSize) {
726 if (migrateBackend()) {
727 // either we are migrating or the request is finished/aborted
728 if (state == Reconnecting || state == WaitingForSession) {
729 resumeNotificationHandling();
730 return; // exit early if we are migrating.
733 error(QNetworkReply::TemporaryNetworkFailureError,
734 QNetworkReply::tr("Temporary network failure."));
741 resumeNotificationHandling();
744 q->setFinished(true);
746 pendingNotifications.clear();
748 pauseNotificationHandling();
749 if (totalSize.isNull() || totalSize == -1) {
750 emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
753 if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
754 emit q->uploadProgress(0, 0);
755 resumeNotificationHandling();
757 // if we don't know the total size of or we received everything save the cache
758 if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
761 // note: might not be a good idea, since users could decide to delete us
762 // which would delete the backend too...
763 // maybe we should protect the backend
764 pauseNotificationHandling();
765 emit q->readChannelFinished();
767 resumeNotificationHandling();
770 void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
772 Q_Q(QNetworkReplyImpl);
773 // Can't set and emit multiple errors.
774 if (errorCode != QNetworkReply::NoError) {
775 qWarning() << "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.";
780 q->setErrorString(errorMessage);
782 // note: might not be a good idea, since users could decide to delete us
783 // which would delete the backend too...
784 // maybe we should protect the backend
788 void QNetworkReplyImplPrivate::metaDataChanged()
790 Q_Q(QNetworkReplyImpl);
791 // 1. do we have cookies?
792 // 2. are we allowed to set them?
793 if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()
794 && (static_cast<QNetworkRequest::LoadControl>
795 (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
796 QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
797 QList<QNetworkCookie> cookies =
798 qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
799 QNetworkCookieJar *jar = manager->cookieJar();
801 jar->setCookiesFromUrl(cookies, url);
803 emit q->metaDataChanged();
806 void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
808 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
811 void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
813 #ifndef QT_NO_OPENSSL
814 Q_Q(QNetworkReplyImpl);
815 emit q->sslErrors(errors);
821 QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
822 : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
826 QNetworkReplyImpl::~QNetworkReplyImpl()
828 Q_D(QNetworkReplyImpl);
830 // This code removes the data from the cache if it was prematurely aborted.
831 // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
832 // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
833 if (d->isCachingEnabled())
834 d->networkCache()->remove(url());
837 void QNetworkReplyImpl::abort()
839 Q_D(QNetworkReplyImpl);
840 if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
843 // stop both upload and download
845 disconnect(d->outgoingData, 0, this, 0);
847 disconnect(d->copyDevice, 0, this, 0);
849 QNetworkReply::close();
851 if (d->state != QNetworkReplyImplPrivate::Finished) {
852 // call finished which will emit signals
853 d->error(OperationCanceledError, tr("Operation canceled"));
856 d->state = QNetworkReplyImplPrivate::Aborted;
858 // finished may access the backend
860 d->backend->deleteLater();
865 void QNetworkReplyImpl::close()
867 Q_D(QNetworkReplyImpl);
868 if (d->state == QNetworkReplyImplPrivate::Aborted ||
869 d->state == QNetworkReplyImplPrivate::Finished)
874 d->backend->closeDownstreamChannel();
876 disconnect(d->copyDevice, 0, this, 0);
878 QNetworkReply::close();
880 // call finished which will emit signals
881 d->error(OperationCanceledError, tr("Operation canceled"));
885 bool QNetworkReplyImpl::canReadLine () const
887 Q_D(const QNetworkReplyImpl);
888 return QNetworkReply::canReadLine() || d->readBuffer.canReadLine();
893 Returns the number of bytes available for reading with
894 QIODevice::read(). The number of bytes available may grow until
895 the finished() signal is emitted.
897 qint64 QNetworkReplyImpl::bytesAvailable() const
899 // Special case for the "zero copy" download buffer
900 Q_D(const QNetworkReplyImpl);
901 if (d->downloadBuffer) {
902 qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
903 return QNetworkReply::bytesAvailable() + maxAvail;
906 return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
909 void QNetworkReplyImpl::setReadBufferSize(qint64 size)
911 Q_D(QNetworkReplyImpl);
912 if (size > d->readBufferMaxSize &&
913 size > d->readBuffer.byteAmount())
914 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
916 QNetworkReply::setReadBufferSize(size);
919 d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
922 #ifndef QT_NO_OPENSSL
923 QSslConfiguration QNetworkReplyImpl::sslConfigurationImplementation() const
925 Q_D(const QNetworkReplyImpl);
926 QSslConfiguration config;
928 d->backend->fetchSslConfiguration(config);
932 void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
934 Q_D(QNetworkReplyImpl);
935 if (d->backend && !config.isNull())
936 d->backend->setSslConfiguration(config);
939 void QNetworkReplyImpl::ignoreSslErrors()
941 Q_D(QNetworkReplyImpl);
943 d->backend->ignoreSslErrors();
946 void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
948 Q_D(QNetworkReplyImpl);
950 d->backend->ignoreSslErrors(errors);
952 #endif // QT_NO_OPENSSL
957 qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
959 Q_D(QNetworkReplyImpl);
961 // Special case code if we have the "zero copy" download buffer
962 if (d->downloadBuffer) {
963 qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
965 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
966 // FIXME what about "Aborted" state?
967 qMemCopy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
968 d->downloadBufferReadPosition += maxAvail;
973 if (d->readBuffer.isEmpty())
974 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
975 // FIXME what about "Aborted" state?
977 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
979 // optimization for getChar()
980 *data = d->readBuffer.getChar();
984 maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
985 return d->readBuffer.read(data, maxlen);
989 \internal Reimplemented for internal purposes
991 bool QNetworkReplyImpl::event(QEvent *e)
993 if (e->type() == QEvent::NetworkReplyUpdated) {
994 d_func()->handleNotifications();
998 return QObject::event(e);
1002 Migrates the backend of the QNetworkReply to a new network connection if required. Returns
1003 true if the reply is migrated or it is not required; otherwise returns false.
1005 bool QNetworkReplyImplPrivate::migrateBackend()
1007 Q_Q(QNetworkReplyImpl);
1009 // Network reply is already finished or aborted, don't need to migrate.
1010 if (state == Finished || state == Aborted)
1013 // Backend does not support resuming download.
1014 if (!backend->canResume())
1017 // Request has outgoing data, not migrating.
1021 // Request is serviced from the cache, don't need to migrate.
1025 state = QNetworkReplyImplPrivate::Reconnecting;
1032 cookedHeaders.clear();
1035 preMigrationDownloaded = bytesDownloaded;
1037 backend = manager->d_func()->findBackend(operation, request);
1040 backend->setParent(q);
1041 backend->reply = this;
1042 backend->setResumeOffset(bytesDownloaded);
1046 if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) {
1047 _q_startOperation();
1049 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1052 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1053 #endif // QT_NO_HTTP
1058 #ifndef QT_NO_BEARERMANAGEMENT
1059 QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent,
1060 const QNetworkRequest &req,
1061 QNetworkAccessManager::Operation op)
1062 : QNetworkReply(parent)
1068 qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
1070 QString msg = QCoreApplication::translate("QNetworkAccessManager",
1071 "Network access is disabled.");
1072 setError(UnknownNetworkError, msg);
1074 QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
1075 Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
1076 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
1079 QDisabledNetworkReply::~QDisabledNetworkReply()
1086 #include "moc_qnetworkreplyimpl_p.cpp"