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 "qnetworkaccessmanager_p.h"
52 #include <QtCore/QCoreApplication>
54 Q_DECLARE_METATYPE(QSharedPointer<char>)
58 inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
59 : backend(0), outgoingData(0),
61 cacheEnabled(false), cacheSaveDevice(0),
62 notificationHandlingPaused(false),
63 bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
66 , downloadBufferReadPosition(0)
67 , downloadBufferCurrentSize(0)
68 , downloadBufferMaximumSize(0)
73 void QNetworkReplyImplPrivate::_q_startOperation()
75 // ensure this function is only being called once
76 if (state == Working || state == Finished) {
77 qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
82 // note: if that method is called directly, it cannot happen that the backend is 0,
83 // because we just checked via a qobject_cast that we got a http backend (see
84 // QNetworkReplyImplPrivate::setup())
86 error(QNetworkReplyImpl::ProtocolUnknownError,
87 QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;
92 if (!backend->start()) {
93 #ifndef QT_NO_BEARERMANAGEMENT
94 // backend failed to start because the session state is not Connected.
95 // QNetworkAccessManager will call _q_startOperation again for us when the session
97 state = WaitingForSession;
99 QNetworkSession *session = manager->d_func()->networkSession.data();
102 Q_Q(QNetworkReplyImpl);
104 QObject::connect(session, SIGNAL(error(QNetworkSession::SessionError)),
105 q, SLOT(_q_networkSessionFailed()));
107 if (!session->isOpen())
110 qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
112 error(QNetworkReplyImpl::UnknownNetworkError,
113 QCoreApplication::translate("QNetworkReply", "Network session error."));
117 qWarning("Backend start failed");
119 error(QNetworkReplyImpl::UnknownNetworkError,
120 QCoreApplication::translate("QNetworkReply", "backend start error."));
126 if (backend && backend->isSynchronous()) {
128 q_func()->setFinished(true);
130 if (state != Finished) {
131 if (operation == QNetworkAccessManager::GetOperation)
132 pendingNotifications.append(NotifyDownstreamReadyWrite);
134 handleNotifications();
139 void QNetworkReplyImplPrivate::_q_copyReadyRead()
141 Q_Q(QNetworkReplyImpl);
142 if (state != Working)
144 if (!copyDevice || !q->isOpen())
147 // FIXME Optimize to use download buffer if it is a QBuffer.
148 // Needs to be done where sendCacheContents() (?) of HTTP is emitting
152 qint64 bytesToRead = nextDownstreamBlockSize();
153 if (bytesToRead == 0)
154 // we'll be called again, eventually
157 bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
159 byteData.resize(bytesToRead);
160 qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
161 if (bytesActuallyRead == -1) {
163 backendNotify(NotifyCopyFinished);
167 byteData.resize(bytesActuallyRead);
168 readBuffer.append(byteData);
170 if (!copyDevice->isSequential() && copyDevice->atEnd()) {
171 backendNotify(NotifyCopyFinished);
172 bytesDownloaded += bytesActuallyRead;
176 bytesDownloaded += bytesActuallyRead;
179 if (bytesDownloaded == lastBytesDownloaded) {
180 // we didn't read anything
184 lastBytesDownloaded = bytesDownloaded;
185 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
186 if (preMigrationDownloaded != Q_INT64_C(-1))
187 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
188 pauseNotificationHandling();
189 // emit readyRead before downloadProgress incase this will cause events to be
190 // processed and we get into a recursive call (as in QProgressDialog).
192 emit q->downloadProgress(bytesDownloaded,
193 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
194 resumeNotificationHandling();
197 void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
202 void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
204 Q_Q(QNetworkReplyImpl);
206 // make sure this is only called once, ever.
207 //_q_bufferOutgoingData may call it or the readChannelFinished emission
208 if (state != Buffering)
211 // disconnect signals
212 QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
213 QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
215 // finally, start the request
216 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
219 void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
221 Q_Q(QNetworkReplyImpl);
223 if (!outgoingDataBuffer) {
224 // first call, create our buffer
225 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
227 QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
228 QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
231 qint64 bytesBuffered = 0;
232 qint64 bytesToBuffer = 0;
234 // read data into our buffer
236 bytesToBuffer = outgoingData->bytesAvailable();
237 // unknown? just try 2 kB, this also ensures we always try to read the EOF
238 if (bytesToBuffer <= 0)
239 bytesToBuffer = 2*1024;
241 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
242 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
244 if (bytesBuffered == -1) {
245 // EOF has been reached.
246 outgoingDataBuffer->chop(bytesToBuffer);
248 _q_bufferOutgoingDataFinished();
250 } else if (bytesBuffered == 0) {
251 // nothing read right now, just wait until we get called again
252 outgoingDataBuffer->chop(bytesToBuffer);
256 // don't break, try to read() again
257 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
262 #ifndef QT_NO_BEARERMANAGEMENT
263 void QNetworkReplyImplPrivate::_q_networkSessionConnected()
265 Q_Q(QNetworkReplyImpl);
267 if (manager.isNull())
270 QNetworkSession *session = manager->d_func()->networkSession.data();
274 if (session->state() != QNetworkSession::Connected)
278 case QNetworkReplyImplPrivate::Buffering:
279 case QNetworkReplyImplPrivate::Working:
280 case QNetworkReplyImplPrivate::Reconnecting:
281 // Migrate existing downloads to new network connection.
284 case QNetworkReplyImplPrivate::WaitingForSession:
285 // Start waiting requests.
286 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
293 void QNetworkReplyImplPrivate::_q_networkSessionFailed()
295 // Abort waiting and working replies.
296 if (state == WaitingForSession || state == Working) {
298 error(QNetworkReplyImpl::UnknownNetworkError,
299 QCoreApplication::translate("QNetworkReply", "Network session error."));
305 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
308 Q_Q(QNetworkReplyImpl);
315 q->QIODevice::open(QIODevice::ReadOnly);
316 // Internal code that does a HTTP reply for the synchronous Ajax
318 QVariant synchronousHttpAttribute = req.attribute(
319 static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
320 // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
321 // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
322 if (synchronousHttpAttribute.toBool() && outgoingData) {
323 outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
324 qint64 previousDataSize = 0;
326 previousDataSize = outgoingDataBuffer->size();
327 outgoingDataBuffer->append(outgoingData->readAll());
328 } while (outgoingDataBuffer->size() != previousDataSize);
332 backend->setSynchronous(synchronousHttpAttribute.toBool());
335 if (outgoingData && backend && !backend->isSynchronous()) {
336 // there is data to be uploaded, e.g. HTTP POST.
338 if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
339 // backend does not need upload buffering or
340 // fixed size non-sequential
341 // just start the operation
342 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
344 bool bufferingDisallowed =
345 req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
348 if (bufferingDisallowed) {
349 // if a valid content-length header for the request was supplied, we can disable buffering
350 // if not, we will buffer anyway
351 if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
352 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
355 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
358 // _q_startOperation will be called when the buffering has finished.
360 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
364 // for HTTP, we want to send out the request as fast as possible to the network, without
365 // invoking methods in a QueuedConnection
367 if (backend && backend->isSynchronous()) {
370 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
373 if (backend && backend->isSynchronous())
376 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
381 void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
383 Q_Q(QNetworkReplyImpl);
384 if (!pendingNotifications.contains(notification))
385 pendingNotifications.enqueue(notification);
387 if (pendingNotifications.size() == 1)
388 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
391 void QNetworkReplyImplPrivate::handleNotifications()
393 if (notificationHandlingPaused)
396 NotificationQueue current = pendingNotifications;
397 pendingNotifications.clear();
399 if (state != Working)
402 while (state == Working && !current.isEmpty()) {
403 InternalNotifications notification = current.dequeue();
404 switch (notification) {
405 case NotifyDownstreamReadyWrite:
409 backend->downstreamReadyWrite();
412 case NotifyCloseDownstreamChannel:
413 backend->closeDownstreamChannel();
416 case NotifyCopyFinished: {
417 QIODevice *dev = copyDevice;
419 backend->copyFinished(dev);
426 // Do not handle the notifications while we are emitting downloadProgress
428 void QNetworkReplyImplPrivate::pauseNotificationHandling()
430 notificationHandlingPaused = true;
433 // Resume notification handling
434 void QNetworkReplyImplPrivate::resumeNotificationHandling()
436 Q_Q(QNetworkReplyImpl);
437 notificationHandlingPaused = false;
438 if (pendingNotifications.size() >= 1)
439 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
442 QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
446 return backend->networkCache();
449 void QNetworkReplyImplPrivate::createCache()
451 // check if we can save and if we're allowed to
453 || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()
454 || request.attribute(QNetworkRequest::CacheLoadControlAttribute,
455 QNetworkRequest::PreferNetwork).toInt()
456 == QNetworkRequest::AlwaysNetwork)
461 bool QNetworkReplyImplPrivate::isCachingEnabled() const
463 return (cacheEnabled && networkCache() != 0);
466 void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
468 if (!enable && !cacheEnabled)
469 return; // nothing to do
470 if (enable && cacheEnabled)
471 return; // nothing to do either!
474 if (bytesDownloaded) {
475 // refuse to enable in this case
476 qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
482 // someone told us to turn on, then back off?
483 // ok... but you should make up your mind
484 qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
485 "backend %s probably needs to be fixed",
486 backend->metaObject()->className());
487 networkCache()->remove(url);
489 cacheEnabled = false;
493 void QNetworkReplyImplPrivate::completeCacheSave()
495 if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
496 networkCache()->remove(url);
497 } else if (cacheEnabled && cacheSaveDevice) {
498 networkCache()->insert(cacheSaveDevice);
501 cacheEnabled = false;
504 void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
506 Q_Q(QNetworkReplyImpl);
507 bytesUploaded = bytesSent;
508 pauseNotificationHandling();
509 emit q->uploadProgress(bytesSent, bytesTotal);
510 resumeNotificationHandling();
514 qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
516 enum { DesiredBufferSize = 32 * 1024 };
517 if (readBufferMaxSize == 0)
518 return DesiredBufferSize;
520 return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
523 void QNetworkReplyImplPrivate::initCacheSaveDevice()
525 Q_Q(QNetworkReplyImpl);
527 // The disk cache does not support partial content, so don't even try to
528 // save any such content into the cache.
529 if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
530 cacheEnabled = false;
534 // save the meta data
535 QNetworkCacheMetaData metaData;
536 metaData.setUrl(url);
537 metaData = backend->fetchCacheMetaData(metaData);
539 // save the redirect request also in the cache
540 QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
541 if (redirectionTarget.isValid()) {
542 QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
543 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
544 metaData.setAttributes(attributes);
547 cacheSaveDevice = networkCache()->prepare(metaData);
549 if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
550 if (cacheSaveDevice && !cacheSaveDevice->isOpen())
551 qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
552 "class %s probably needs to be fixed",
553 networkCache()->metaObject()->className());
555 networkCache()->remove(url);
557 cacheEnabled = false;
561 // we received downstream data and send this to the cache
562 // and to our readBuffer (which in turn gets read by the user of QNetworkReply)
563 void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
565 Q_Q(QNetworkReplyImpl);
569 if (cacheEnabled && !cacheSaveDevice) {
570 initCacheSaveDevice();
573 qint64 bytesWritten = 0;
574 for (int i = 0; i < data.bufferCount(); i++) {
575 QByteArray const &item = data[i];
578 cacheSaveDevice->write(item.constData(), item.size());
579 readBuffer.append(item);
581 bytesWritten += item.size();
585 bytesDownloaded += bytesWritten;
586 lastBytesDownloaded = bytesDownloaded;
588 appendDownstreamDataSignalEmissions();
591 void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
593 Q_Q(QNetworkReplyImpl);
595 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
596 if (preMigrationDownloaded != Q_INT64_C(-1))
597 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
598 pauseNotificationHandling();
599 // important: At the point of this readyRead(), the data parameter list must be empty,
600 // else implicit sharing will trigger memcpy when the user is reading data!
602 // emit readyRead before downloadProgress incase this will cause events to be
603 // processed and we get into a recursive call (as in QProgressDialog).
604 emit q->downloadProgress(bytesDownloaded,
605 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
607 resumeNotificationHandling();
608 // do we still have room in the buffer?
609 if (nextDownstreamBlockSize() > 0)
610 backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
613 // this is used when it was fetched from the cache, right?
614 void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
616 Q_Q(QNetworkReplyImpl);
620 // read until EOF from data
622 qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
623 "backend probly needs to be fixed");
628 q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
629 q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
635 void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
642 qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
645 static void downloadBufferDeleter(char *ptr)
650 char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
652 Q_Q(QNetworkReplyImpl);
654 if (!downloadBuffer) {
655 // We are requested to create it
656 // Check attribute() if allocating a buffer of that size can be allowed
657 QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
658 if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
659 downloadBufferCurrentSize = 0;
660 downloadBufferMaximumSize = size;
661 downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
662 downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
664 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
668 return downloadBuffer;
671 void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
673 Q_Q(QNetworkReplyImpl);
675 downloadBufferPointer = sp;
676 downloadBuffer = downloadBufferPointer.data();
677 downloadBufferCurrentSize = 0;
678 downloadBufferMaximumSize = size;
679 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
683 void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
685 Q_Q(QNetworkReplyImpl);
689 if (cacheEnabled && !cacheSaveDevice)
690 initCacheSaveDevice();
692 if (cacheSaveDevice && bytesReceived == bytesTotal) {
693 // if (lastBytesDownloaded == -1)
694 // lastBytesDownloaded = 0;
695 // cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
697 // Write everything in one go if we use a download buffer. might be more performant.
698 cacheSaveDevice->write(downloadBuffer, bytesTotal);
701 bytesDownloaded = bytesReceived;
702 lastBytesDownloaded = bytesReceived;
704 downloadBufferCurrentSize = bytesReceived;
706 // Only emit readyRead when actual data is there
707 // emit readyRead before downloadProgress incase this will cause events to be
708 // processed and we get into a recursive call (as in QProgressDialog).
709 if (bytesDownloaded > 0)
711 emit q->downloadProgress(bytesDownloaded, bytesTotal);
714 void QNetworkReplyImplPrivate::finished()
716 Q_Q(QNetworkReplyImpl);
718 if (state == Finished || state == Aborted || state == WaitingForSession)
721 pauseNotificationHandling();
722 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
723 if (preMigrationDownloaded != Q_INT64_C(-1))
724 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
726 if (!manager.isNull()) {
727 #ifndef QT_NO_BEARERMANAGEMENT
728 QNetworkSession *session = manager->d_func()->networkSession.data();
729 if (session && session->state() == QNetworkSession::Roaming &&
730 state == Working && errorCode != QNetworkReply::OperationCanceledError) {
731 // only content with a known size will fail with a temporary network failure error
732 if (!totalSize.isNull()) {
733 if (bytesDownloaded != totalSize) {
734 if (migrateBackend()) {
735 // either we are migrating or the request is finished/aborted
736 if (state == Reconnecting || state == WaitingForSession) {
737 resumeNotificationHandling();
738 return; // exit early if we are migrating.
741 error(QNetworkReply::TemporaryNetworkFailureError,
742 QNetworkReply::tr("Temporary network failure."));
749 resumeNotificationHandling();
752 q->setFinished(true);
754 pendingNotifications.clear();
756 pauseNotificationHandling();
757 if (totalSize.isNull() || totalSize == -1) {
758 emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
761 if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
762 emit q->uploadProgress(0, 0);
763 resumeNotificationHandling();
765 // if we don't know the total size of or we received everything save the cache
766 if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
769 // note: might not be a good idea, since users could decide to delete us
770 // which would delete the backend too...
771 // maybe we should protect the backend
772 pauseNotificationHandling();
773 emit q->readChannelFinished();
775 resumeNotificationHandling();
778 void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
780 Q_Q(QNetworkReplyImpl);
781 // Can't set and emit multiple errors.
782 if (errorCode != QNetworkReply::NoError) {
783 qWarning() << "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.";
788 q->setErrorString(errorMessage);
790 // note: might not be a good idea, since users could decide to delete us
791 // which would delete the backend too...
792 // maybe we should protect the backend
796 void QNetworkReplyImplPrivate::metaDataChanged()
798 Q_Q(QNetworkReplyImpl);
799 // 1. do we have cookies?
800 // 2. are we allowed to set them?
801 if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()
802 && (static_cast<QNetworkRequest::LoadControl>
803 (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
804 QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
805 QList<QNetworkCookie> cookies =
806 qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
807 QNetworkCookieJar *jar = manager->cookieJar();
809 jar->setCookiesFromUrl(cookies, url);
811 emit q->metaDataChanged();
814 void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
816 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
819 void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
821 #ifndef QT_NO_OPENSSL
822 Q_Q(QNetworkReplyImpl);
823 emit q->sslErrors(errors);
829 QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
830 : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
834 QNetworkReplyImpl::~QNetworkReplyImpl()
836 Q_D(QNetworkReplyImpl);
838 // This code removes the data from the cache if it was prematurely aborted.
839 // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
840 // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
841 if (d->isCachingEnabled())
842 d->networkCache()->remove(url());
845 void QNetworkReplyImpl::abort()
847 Q_D(QNetworkReplyImpl);
848 if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
851 // stop both upload and download
853 disconnect(d->outgoingData, 0, this, 0);
855 disconnect(d->copyDevice, 0, this, 0);
857 QNetworkReply::close();
859 if (d->state != QNetworkReplyImplPrivate::Finished) {
860 // call finished which will emit signals
861 d->error(OperationCanceledError, tr("Operation canceled"));
864 d->state = QNetworkReplyImplPrivate::Aborted;
866 // finished may access the backend
868 d->backend->deleteLater();
873 void QNetworkReplyImpl::close()
875 Q_D(QNetworkReplyImpl);
876 if (d->state == QNetworkReplyImplPrivate::Aborted ||
877 d->state == QNetworkReplyImplPrivate::Finished)
882 d->backend->closeDownstreamChannel();
884 disconnect(d->copyDevice, 0, this, 0);
886 QNetworkReply::close();
888 // call finished which will emit signals
889 d->error(OperationCanceledError, tr("Operation canceled"));
893 bool QNetworkReplyImpl::canReadLine () const
895 Q_D(const QNetworkReplyImpl);
896 return QNetworkReply::canReadLine() || d->readBuffer.canReadLine();
901 Returns the number of bytes available for reading with
902 QIODevice::read(). The number of bytes available may grow until
903 the finished() signal is emitted.
905 qint64 QNetworkReplyImpl::bytesAvailable() const
907 // Special case for the "zero copy" download buffer
908 Q_D(const QNetworkReplyImpl);
909 if (d->downloadBuffer) {
910 qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
911 return QNetworkReply::bytesAvailable() + maxAvail;
914 return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
917 void QNetworkReplyImpl::setReadBufferSize(qint64 size)
919 Q_D(QNetworkReplyImpl);
920 if (size > d->readBufferMaxSize &&
921 size > d->readBuffer.byteAmount())
922 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
924 QNetworkReply::setReadBufferSize(size);
927 d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
930 #ifndef QT_NO_OPENSSL
931 QSslConfiguration QNetworkReplyImpl::sslConfigurationImplementation() const
933 Q_D(const QNetworkReplyImpl);
934 QSslConfiguration config;
936 d->backend->fetchSslConfiguration(config);
940 void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
942 Q_D(QNetworkReplyImpl);
943 if (d->backend && !config.isNull())
944 d->backend->setSslConfiguration(config);
947 void QNetworkReplyImpl::ignoreSslErrors()
949 Q_D(QNetworkReplyImpl);
951 d->backend->ignoreSslErrors();
954 void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
956 Q_D(QNetworkReplyImpl);
958 d->backend->ignoreSslErrors(errors);
960 #endif // QT_NO_OPENSSL
965 qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
967 Q_D(QNetworkReplyImpl);
969 // Special case code if we have the "zero copy" download buffer
970 if (d->downloadBuffer) {
971 qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
973 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
974 // FIXME what about "Aborted" state?
975 qMemCopy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
976 d->downloadBufferReadPosition += maxAvail;
981 if (d->readBuffer.isEmpty())
982 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
983 // FIXME what about "Aborted" state?
985 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
987 // optimization for getChar()
988 *data = d->readBuffer.getChar();
992 maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
993 return d->readBuffer.read(data, maxlen);
997 \internal Reimplemented for internal purposes
999 bool QNetworkReplyImpl::event(QEvent *e)
1001 if (e->type() == QEvent::NetworkReplyUpdated) {
1002 d_func()->handleNotifications();
1006 return QObject::event(e);
1010 Migrates the backend of the QNetworkReply to a new network connection if required. Returns
1011 true if the reply is migrated or it is not required; otherwise returns false.
1013 bool QNetworkReplyImplPrivate::migrateBackend()
1015 Q_Q(QNetworkReplyImpl);
1017 // Network reply is already finished or aborted, don't need to migrate.
1018 if (state == Finished || state == Aborted)
1021 // Backend does not support resuming download.
1022 if (!backend->canResume())
1025 // Request has outgoing data, not migrating.
1029 // Request is serviced from the cache, don't need to migrate.
1033 state = QNetworkReplyImplPrivate::Reconnecting;
1040 cookedHeaders.clear();
1043 preMigrationDownloaded = bytesDownloaded;
1045 backend = manager->d_func()->findBackend(operation, request);
1048 backend->setParent(q);
1049 backend->reply = this;
1050 backend->setResumeOffset(bytesDownloaded);
1054 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1056 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1057 #endif // QT_NO_HTTP
1062 #ifndef QT_NO_BEARERMANAGEMENT
1063 QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent,
1064 const QNetworkRequest &req,
1065 QNetworkAccessManager::Operation op)
1066 : QNetworkReply(parent)
1072 qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
1074 QString msg = QCoreApplication::translate("QNetworkAccessManager",
1075 "Network access is disabled.");
1076 setError(UnknownNetworkError, msg);
1078 QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
1079 Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
1080 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
1083 QDisabledNetworkReply::~QDisabledNetworkReply()
1090 #include "moc_qnetworkreplyimpl_p.cpp"