choke uploadProgress signals
[profile/ivi/qtbase.git] / src / network / access / qnetworkreplyimpl.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
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"
52
53 #include <QtCore/QCoreApplication>
54
55 Q_DECLARE_METATYPE(QSharedPointer<char>)
56
57 QT_BEGIN_NAMESPACE
58
59 inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
60     : backend(0), outgoingData(0),
61       copyDevice(0),
62       cacheEnabled(false), cacheSaveDevice(0),
63       notificationHandlingPaused(false),
64       bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
65       httpStatusCode(0),
66       state(Idle)
67       , downloadBufferReadPosition(0)
68       , downloadBufferCurrentSize(0)
69       , downloadBufferMaximumSize(0)
70       , downloadBuffer(0)
71 {
72 }
73
74 void QNetworkReplyImplPrivate::_q_startOperation()
75 {
76     Q_Q(QNetworkReplyImpl);
77
78     // ensure this function is only being called once
79     if (state == Working || state == Finished) {
80         qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
81         return;
82     }
83     state = Working;
84
85     // note: if that method is called directly, it cannot happen that the backend is 0,
86     // because we just checked via a qobject_cast that we got a http backend (see
87     // QNetworkReplyImplPrivate::setup())
88     if (!backend) {
89         error(QNetworkReplyImpl::ProtocolUnknownError,
90               QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;
91         finished();
92         return;
93     }
94
95 #ifndef QT_NO_BEARERMANAGEMENT
96     // Do not start background requests if they are not allowed by session policy
97     QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
98     QVariant isBackground = backend->request().attribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(false));
99     if (isBackground.toBool() && session && session->usagePolicies().testFlag(QNetworkSession::NoBackgroundTrafficPolicy)) {
100         error(QNetworkReply::BackgroundRequestNotAllowedError,
101             QCoreApplication::translate("QNetworkReply", "Background request not allowed."));
102         finished();
103         return;
104     }
105 #endif
106
107     if (!backend->start()) {
108 #ifndef QT_NO_BEARERMANAGEMENT
109         // backend failed to start because the session state is not Connected.
110         // QNetworkAccessManager will call _q_startOperation again for us when the session
111         // state changes.
112         state = WaitingForSession;
113
114         if (session) {
115             QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)),
116                              q, SLOT(_q_networkSessionFailed()));
117
118             if (!session->isOpen()) {
119                 session->setSessionProperty(QStringLiteral("ConnectInBackground"), isBackground);
120                 session->open();
121             }
122         } else {
123             qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
124             state = Working;
125             error(QNetworkReplyImpl::NetworkSessionFailedError,
126                   QCoreApplication::translate("QNetworkReply", "Network session error."));
127             finished();
128         }
129 #else
130         qWarning("Backend start failed");
131         state = Working;
132         error(QNetworkReplyImpl::UnknownNetworkError,
133               QCoreApplication::translate("QNetworkReply", "backend start error."));
134         finished();
135 #endif
136         return;
137     }
138
139 #ifndef QT_NO_BEARERMANAGEMENT
140     if (session) {
141         //get notification of policy changes.
142         QObject::connect(session.data(), SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)),
143                     q, SLOT(_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies)));
144     }
145 #endif
146
147     // Prepare timer for progress notifications
148     downloadProgressSignalChoke.start();
149     uploadProgressSignalChoke.invalidate();
150
151     if (backend && backend->isSynchronous()) {
152         state = Finished;
153         q_func()->setFinished(true);
154     } else {
155         if (state != Finished) {
156             if (operation == QNetworkAccessManager::GetOperation)
157                 pendingNotifications.append(NotifyDownstreamReadyWrite);
158
159             handleNotifications();
160         }
161     }
162 }
163
164 void QNetworkReplyImplPrivate::_q_copyReadyRead()
165 {
166     Q_Q(QNetworkReplyImpl);
167     if (state != Working)
168         return;
169     if (!copyDevice || !q->isOpen())
170         return;
171
172     // FIXME Optimize to use download buffer if it is a QBuffer.
173     // Needs to be done where sendCacheContents() (?) of HTTP is emitting
174     // metaDataChanged ?
175
176     forever {
177         qint64 bytesToRead = nextDownstreamBlockSize();
178         if (bytesToRead == 0)
179             // we'll be called again, eventually
180             break;
181
182         bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
183         QByteArray byteData;
184         byteData.resize(bytesToRead);
185         qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
186         if (bytesActuallyRead == -1) {
187             byteData.clear();
188             backendNotify(NotifyCopyFinished);
189             break;
190         }
191
192         byteData.resize(bytesActuallyRead);
193         readBuffer.append(byteData);
194
195         if (!copyDevice->isSequential() && copyDevice->atEnd()) {
196             backendNotify(NotifyCopyFinished);
197             bytesDownloaded += bytesActuallyRead;
198             break;
199         }
200
201         bytesDownloaded += bytesActuallyRead;
202     }
203
204     if (bytesDownloaded == lastBytesDownloaded) {
205         // we didn't read anything
206         return;
207     }
208
209     lastBytesDownloaded = bytesDownloaded;
210     QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
211     if (preMigrationDownloaded != Q_INT64_C(-1))
212         totalSize = totalSize.toLongLong() + preMigrationDownloaded;
213     pauseNotificationHandling();
214     // emit readyRead before downloadProgress incase this will cause events to be
215     // processed and we get into a recursive call (as in QProgressDialog).
216     emit q->readyRead();
217     if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
218         downloadProgressSignalChoke.restart();
219         emit q->downloadProgress(bytesDownloaded,
220                              totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
221     }
222     resumeNotificationHandling();
223 }
224
225 void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
226 {
227     _q_copyReadyRead();
228 }
229
230 void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
231 {
232     Q_Q(QNetworkReplyImpl);
233
234     // make sure this is only called once, ever.
235     //_q_bufferOutgoingData may call it or the readChannelFinished emission
236     if (state != Buffering)
237         return;
238
239     // disconnect signals
240     QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
241     QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
242
243     // finally, start the request
244     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
245 }
246
247 void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
248 {
249     Q_Q(QNetworkReplyImpl);
250
251     if (!outgoingDataBuffer) {
252         // first call, create our buffer
253         outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
254
255         QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
256         QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
257     }
258
259     qint64 bytesBuffered = 0;
260     qint64 bytesToBuffer = 0;
261
262     // read data into our buffer
263     forever {
264         bytesToBuffer = outgoingData->bytesAvailable();
265         // unknown? just try 2 kB, this also ensures we always try to read the EOF
266         if (bytesToBuffer <= 0)
267             bytesToBuffer = 2*1024;
268
269         char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
270         bytesBuffered = outgoingData->read(dst, bytesToBuffer);
271
272         if (bytesBuffered == -1) {
273             // EOF has been reached.
274             outgoingDataBuffer->chop(bytesToBuffer);
275
276             _q_bufferOutgoingDataFinished();
277             break;
278         } else if (bytesBuffered == 0) {
279             // nothing read right now, just wait until we get called again
280             outgoingDataBuffer->chop(bytesToBuffer);
281
282             break;
283         } else {
284             // don't break, try to read() again
285             outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
286         }
287     }
288 }
289
290 #ifndef QT_NO_BEARERMANAGEMENT
291 void QNetworkReplyImplPrivate::_q_networkSessionConnected()
292 {
293     Q_Q(QNetworkReplyImpl);
294
295     if (manager.isNull())
296         return;
297
298     QSharedPointer<QNetworkSession> session = manager->d_func()->getNetworkSession();
299     if (!session)
300         return;
301
302     if (session->state() != QNetworkSession::Connected)
303         return;
304
305     switch (state) {
306     case QNetworkReplyImplPrivate::Buffering:
307     case QNetworkReplyImplPrivate::Working:
308     case QNetworkReplyImplPrivate::Reconnecting:
309         // Migrate existing downloads to new network connection.
310         migrateBackend();
311         break;
312     case QNetworkReplyImplPrivate::WaitingForSession:
313         // Start waiting requests.
314         QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
315         break;
316     default:
317         ;
318     }
319 }
320
321 void QNetworkReplyImplPrivate::_q_networkSessionFailed()
322 {
323     // Abort waiting and working replies.
324     if (state == WaitingForSession || state == Working) {
325         state = Working;
326         QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
327         QString errorStr;
328         if (session)
329             errorStr = session->errorString();
330         else
331             errorStr = QCoreApplication::translate("QNetworkReply", "Network session error.");
332         error(QNetworkReplyImpl::NetworkSessionFailedError, errorStr);
333         finished();
334     }
335 }
336
337 void QNetworkReplyImplPrivate::_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies newPolicies)
338 {
339     if (backend->request().attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) {
340         if (newPolicies & QNetworkSession::NoBackgroundTrafficPolicy) {
341             // Abort waiting and working replies.
342             if (state == WaitingForSession || state == Working) {
343                 state = Working;
344                 error(QNetworkReply::BackgroundRequestNotAllowedError,
345                     QCoreApplication::translate("QNetworkReply", "Background request not allowed."));
346                 finished();
347             }
348             // ### if backend->canResume(), then we could resume automatically, however no backend supports resuming
349         }
350     }
351 }
352 #endif
353
354 void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
355                                      QIODevice *data)
356 {
357     Q_Q(QNetworkReplyImpl);
358
359     outgoingData = data;
360     request = req;
361     url = request.url();
362     operation = op;
363
364     q->QIODevice::open(QIODevice::ReadOnly);
365     // Internal code that does a HTTP reply for the synchronous Ajax
366     // in QtWebKit.
367     QVariant synchronousHttpAttribute = req.attribute(
368             static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
369     // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
370     // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
371     if (synchronousHttpAttribute.toBool() && outgoingData) {
372         outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
373         qint64 previousDataSize = 0;
374         do {
375             previousDataSize = outgoingDataBuffer->size();
376             outgoingDataBuffer->append(outgoingData->readAll());
377         } while (outgoingDataBuffer->size() != previousDataSize);
378     }
379
380     if (backend)
381         backend->setSynchronous(synchronousHttpAttribute.toBool());
382
383
384     if (outgoingData && backend && !backend->isSynchronous()) {
385         // there is data to be uploaded, e.g. HTTP POST.
386
387         if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
388             // backend does not need upload buffering or
389             // fixed size non-sequential
390             // just start the operation
391             QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
392         } else {
393             bool bufferingDisallowed =
394                     req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
395                                   false).toBool();
396
397             if (bufferingDisallowed) {
398                 // if a valid content-length header for the request was supplied, we can disable buffering
399                 // if not, we will buffer anyway
400                 if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
401                     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
402                 } else {
403                     state = Buffering;
404                     QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
405                 }
406             } else {
407                 // _q_startOperation will be called when the buffering has finished.
408                 state = Buffering;
409                 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
410             }
411         }
412     } else {
413         // for HTTP, we want to send out the request as fast as possible to the network, without
414         // invoking methods in a QueuedConnection
415 #ifndef QT_NO_HTTP
416         if (backend && backend->isSynchronous()) {
417             _q_startOperation();
418         } else {
419             QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
420         }
421 #else
422         if (backend && backend->isSynchronous())
423             _q_startOperation();
424         else
425             QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
426 #endif // QT_NO_HTTP
427         }
428 }
429
430 void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
431 {
432     Q_Q(QNetworkReplyImpl);
433     if (!pendingNotifications.contains(notification))
434         pendingNotifications.enqueue(notification);
435
436     if (pendingNotifications.size() == 1)
437         QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
438 }
439
440 void QNetworkReplyImplPrivate::handleNotifications()
441 {
442     if (notificationHandlingPaused)
443         return;
444
445     NotificationQueue current = pendingNotifications;
446     pendingNotifications.clear();
447
448     if (state != Working)
449         return;
450
451     while (state == Working && !current.isEmpty()) {
452         InternalNotifications notification = current.dequeue();
453         switch (notification) {
454         case NotifyDownstreamReadyWrite:
455             if (copyDevice)
456                 _q_copyReadyRead();
457             else
458                 backend->downstreamReadyWrite();
459             break;
460
461         case NotifyCloseDownstreamChannel:
462             backend->closeDownstreamChannel();
463             break;
464
465         case NotifyCopyFinished: {
466             QIODevice *dev = copyDevice;
467             copyDevice = 0;
468             backend->copyFinished(dev);
469             break;
470         }
471         }
472     }
473 }
474
475 // Do not handle the notifications while we are emitting downloadProgress
476 // or readyRead
477 void QNetworkReplyImplPrivate::pauseNotificationHandling()
478 {
479     notificationHandlingPaused = true;
480 }
481
482 // Resume notification handling
483 void QNetworkReplyImplPrivate::resumeNotificationHandling()
484 {
485     Q_Q(QNetworkReplyImpl);
486     notificationHandlingPaused = false;
487     if (pendingNotifications.size() >= 1)
488         QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
489 }
490
491 QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
492 {
493     if (!backend)
494         return 0;
495     return backend->networkCache();
496 }
497
498 void QNetworkReplyImplPrivate::createCache()
499 {
500     // check if we can save and if we're allowed to
501     if (!networkCache()
502         || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool())
503         return;
504     cacheEnabled = true;
505 }
506
507 bool QNetworkReplyImplPrivate::isCachingEnabled() const
508 {
509     return (cacheEnabled && networkCache() != 0);
510 }
511
512 void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
513 {
514     if (!enable && !cacheEnabled)
515         return;                 // nothing to do
516     if (enable && cacheEnabled)
517         return;                 // nothing to do either!
518
519     if (enable) {
520         if (bytesDownloaded) {
521             // refuse to enable in this case
522             qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
523             return;
524         }
525
526         createCache();
527     } else {
528         // someone told us to turn on, then back off?
529         // ok... but you should make up your mind
530         qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
531                "backend %s probably needs to be fixed",
532                backend->metaObject()->className());
533         networkCache()->remove(url);
534         cacheSaveDevice = 0;
535         cacheEnabled = false;
536     }
537 }
538
539 void QNetworkReplyImplPrivate::completeCacheSave()
540 {
541     if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
542         networkCache()->remove(url);
543     } else if (cacheEnabled && cacheSaveDevice) {
544         networkCache()->insert(cacheSaveDevice);
545     }
546     cacheSaveDevice = 0;
547     cacheEnabled = false;
548 }
549
550 void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
551 {
552     Q_Q(QNetworkReplyImpl);
553     bytesUploaded = bytesSent;
554
555     //choke signal emissions, except the first and last signals which are unconditional
556     if (uploadProgressSignalChoke.isValid()) {
557         if (bytesSent != bytesTotal && uploadProgressSignalChoke.elapsed() < progressSignalInterval) {
558             return;
559         }
560         uploadProgressSignalChoke.restart();
561     } else {
562         uploadProgressSignalChoke.start();
563     }
564
565     pauseNotificationHandling();
566     emit q->uploadProgress(bytesSent, bytesTotal);
567     resumeNotificationHandling();
568 }
569
570
571 qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
572 {
573     enum { DesiredBufferSize = 32 * 1024 };
574     if (readBufferMaxSize == 0)
575         return DesiredBufferSize;
576
577     return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
578 }
579
580 void QNetworkReplyImplPrivate::initCacheSaveDevice()
581 {
582     Q_Q(QNetworkReplyImpl);
583
584     // The disk cache does not support partial content, so don't even try to
585     // save any such content into the cache.
586     if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
587         cacheEnabled = false;
588         return;
589     }
590
591     // save the meta data
592     QNetworkCacheMetaData metaData;
593     metaData.setUrl(url);
594     metaData = backend->fetchCacheMetaData(metaData);
595
596     // save the redirect request also in the cache
597     QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
598     if (redirectionTarget.isValid()) {
599         QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
600         attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
601         metaData.setAttributes(attributes);
602     }
603
604     cacheSaveDevice = networkCache()->prepare(metaData);
605
606     if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
607         if (cacheSaveDevice && !cacheSaveDevice->isOpen())
608             qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
609                   "class %s probably needs to be fixed",
610                   networkCache()->metaObject()->className());
611
612         networkCache()->remove(url);
613         cacheSaveDevice = 0;
614         cacheEnabled = false;
615     }
616 }
617
618 // we received downstream data and send this to the cache
619 // and to our readBuffer (which in turn gets read by the user of QNetworkReply)
620 void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
621 {
622     Q_Q(QNetworkReplyImpl);
623     if (!q->isOpen())
624         return;
625
626     if (cacheEnabled && !cacheSaveDevice) {
627         initCacheSaveDevice();
628     }
629
630     qint64 bytesWritten = 0;
631     for (int i = 0; i < data.bufferCount(); i++) {
632         QByteArray const &item = data[i];
633
634         if (cacheSaveDevice)
635             cacheSaveDevice->write(item.constData(), item.size());
636         readBuffer.append(item);
637
638         bytesWritten += item.size();
639     }
640     data.clear();
641
642     bytesDownloaded += bytesWritten;
643     lastBytesDownloaded = bytesDownloaded;
644
645     appendDownstreamDataSignalEmissions();
646 }
647
648 void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
649 {
650     Q_Q(QNetworkReplyImpl);
651
652     QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
653     if (preMigrationDownloaded != Q_INT64_C(-1))
654         totalSize = totalSize.toLongLong() + preMigrationDownloaded;
655     pauseNotificationHandling();
656     // important: At the point of this readyRead(), the data parameter list must be empty,
657     // else implicit sharing will trigger memcpy when the user is reading data!
658     emit q->readyRead();
659     // emit readyRead before downloadProgress incase this will cause events to be
660     // processed and we get into a recursive call (as in QProgressDialog).
661     if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
662         downloadProgressSignalChoke.restart();
663         emit q->downloadProgress(bytesDownloaded,
664                              totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
665     }
666
667     resumeNotificationHandling();
668     // do we still have room in the buffer?
669     if (nextDownstreamBlockSize() > 0)
670         backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
671 }
672
673 // this is used when it was fetched from the cache, right?
674 void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
675 {
676     Q_Q(QNetworkReplyImpl);
677     if (!q->isOpen())
678         return;
679
680     // read until EOF from data
681     if (copyDevice) {
682         qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
683                   "backend probly needs to be fixed");
684         return;
685     }
686
687     copyDevice = data;
688     q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
689     q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
690
691     // start the copy:
692     _q_copyReadyRead();
693 }
694
695 void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
696 {
697     Q_UNUSED(data)
698     // TODO implement
699
700     // TODO call
701
702     qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
703 }
704
705 static void downloadBufferDeleter(char *ptr)
706 {
707     delete[] ptr;
708 }
709
710 char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
711 {
712     Q_Q(QNetworkReplyImpl);
713
714     if (!downloadBuffer) {
715         // We are requested to create it
716         // Check attribute() if allocating a buffer of that size can be allowed
717         QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
718         if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
719             downloadBufferCurrentSize = 0;
720             downloadBufferMaximumSize = size;
721             downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
722             downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
723
724             q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
725         }
726     }
727
728     return downloadBuffer;
729 }
730
731 void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
732 {
733     Q_Q(QNetworkReplyImpl);
734
735     downloadBufferPointer = sp;
736     downloadBuffer = downloadBufferPointer.data();
737     downloadBufferCurrentSize = 0;
738     downloadBufferMaximumSize = size;
739     q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
740 }
741
742
743 void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
744 {
745     Q_Q(QNetworkReplyImpl);
746     if (!q->isOpen())
747         return;
748
749     if (cacheEnabled && !cacheSaveDevice)
750         initCacheSaveDevice();
751
752     if (cacheSaveDevice && bytesReceived == bytesTotal) {
753 //        if (lastBytesDownloaded == -1)
754 //            lastBytesDownloaded = 0;
755 //        cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
756
757         // Write everything in one go if we use a download buffer. might be more performant.
758         cacheSaveDevice->write(downloadBuffer, bytesTotal);
759     }
760
761     bytesDownloaded = bytesReceived;
762     lastBytesDownloaded = bytesReceived;
763
764     downloadBufferCurrentSize = bytesReceived;
765
766     // Only emit readyRead when actual data is there
767     // emit readyRead before downloadProgress incase this will cause events to be
768     // processed and we get into a recursive call (as in QProgressDialog).
769     if (bytesDownloaded > 0)
770         emit q->readyRead();
771     if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
772         downloadProgressSignalChoke.restart();
773         emit q->downloadProgress(bytesDownloaded, bytesTotal);
774     }
775 }
776
777 void QNetworkReplyImplPrivate::finished()
778 {
779     Q_Q(QNetworkReplyImpl);
780
781     if (state == Finished || state == Aborted || state == WaitingForSession)
782         return;
783
784     pauseNotificationHandling();
785     QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
786     if (preMigrationDownloaded != Q_INT64_C(-1))
787         totalSize = totalSize.toLongLong() + preMigrationDownloaded;
788
789     if (!manager.isNull()) {
790 #ifndef QT_NO_BEARERMANAGEMENT
791         QSharedPointer<QNetworkSession> session (manager->d_func()->getNetworkSession());
792         if (session && session->state() == QNetworkSession::Roaming &&
793             state == Working && errorCode != QNetworkReply::OperationCanceledError) {
794             // only content with a known size will fail with a temporary network failure error
795             if (!totalSize.isNull()) {
796                 if (bytesDownloaded != totalSize) {
797                     if (migrateBackend()) {
798                         // either we are migrating or the request is finished/aborted
799                         if (state == Reconnecting || state == WaitingForSession) {
800                             resumeNotificationHandling();
801                             return; // exit early if we are migrating.
802                         }
803                     } else {
804                         error(QNetworkReply::TemporaryNetworkFailureError,
805                               QNetworkReply::tr("Temporary network failure."));
806                     }
807                 }
808             }
809         }
810 #endif
811     }
812     resumeNotificationHandling();
813
814     state = Finished;
815     q->setFinished(true);
816
817     pendingNotifications.clear();
818
819     pauseNotificationHandling();
820     if (totalSize.isNull() || totalSize == -1) {
821         emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
822     } else {
823         emit q->downloadProgress(bytesDownloaded, totalSize.toLongLong());
824     }
825
826     if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
827         emit q->uploadProgress(0, 0);
828     resumeNotificationHandling();
829
830     // if we don't know the total size of or we received everything save the cache
831     if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
832         completeCacheSave();
833
834     // note: might not be a good idea, since users could decide to delete us
835     // which would delete the backend too...
836     // maybe we should protect the backend
837     pauseNotificationHandling();
838     emit q->readChannelFinished();
839     emit q->finished();
840     resumeNotificationHandling();
841 }
842
843 void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
844 {
845     Q_Q(QNetworkReplyImpl);
846     // Can't set and emit multiple errors.
847     if (errorCode != QNetworkReply::NoError) {
848         qWarning( "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
849         return;
850     }
851
852     errorCode = code;
853     q->setErrorString(errorMessage);
854
855     // note: might not be a good idea, since users could decide to delete us
856     // which would delete the backend too...
857     // maybe we should protect the backend
858     emit q->error(code);
859 }
860
861 void QNetworkReplyImplPrivate::metaDataChanged()
862 {
863     Q_Q(QNetworkReplyImpl);
864     // 1. do we have cookies?
865     // 2. are we allowed to set them?
866     if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()
867         && (static_cast<QNetworkRequest::LoadControl>
868             (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
869                                QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
870         QList<QNetworkCookie> cookies =
871             qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
872         QNetworkCookieJar *jar = manager->cookieJar();
873         if (jar)
874             jar->setCookiesFromUrl(cookies, url);
875     }
876     emit q->metaDataChanged();
877 }
878
879 void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
880 {
881     attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
882 }
883
884 void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
885 {
886 #ifndef QT_NO_SSL
887     Q_Q(QNetworkReplyImpl);
888     emit q->sslErrors(errors);
889 #else
890     Q_UNUSED(errors);
891 #endif
892 }
893
894 QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
895     : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
896 {
897 }
898
899 QNetworkReplyImpl::~QNetworkReplyImpl()
900 {
901     Q_D(QNetworkReplyImpl);
902
903     // This code removes the data from the cache if it was prematurely aborted.
904     // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
905     // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
906     if (d->isCachingEnabled())
907         d->networkCache()->remove(url());
908 }
909
910 void QNetworkReplyImpl::abort()
911 {
912     Q_D(QNetworkReplyImpl);
913     if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
914         return;
915
916     // stop both upload and download
917     if (d->outgoingData)
918         disconnect(d->outgoingData, 0, this, 0);
919     if (d->copyDevice)
920         disconnect(d->copyDevice, 0, this, 0);
921
922     QNetworkReply::close();
923
924     if (d->state != QNetworkReplyImplPrivate::Finished) {
925         // call finished which will emit signals
926         d->error(OperationCanceledError, tr("Operation canceled"));
927         if (d->state == QNetworkReplyImplPrivate::WaitingForSession)
928             d->state = QNetworkReplyImplPrivate::Working;
929         d->finished();
930     }
931     d->state = QNetworkReplyImplPrivate::Aborted;
932
933     // finished may access the backend
934     if (d->backend) {
935         d->backend->deleteLater();
936         d->backend = 0;
937     }
938 }
939
940 void QNetworkReplyImpl::close()
941 {
942     Q_D(QNetworkReplyImpl);
943     if (d->state == QNetworkReplyImplPrivate::Aborted ||
944         d->state == QNetworkReplyImplPrivate::Finished)
945         return;
946
947     // stop the download
948     if (d->backend)
949         d->backend->closeDownstreamChannel();
950     if (d->copyDevice)
951         disconnect(d->copyDevice, 0, this, 0);
952
953     QNetworkReply::close();
954
955     // call finished which will emit signals
956     d->error(OperationCanceledError, tr("Operation canceled"));
957     d->finished();
958 }
959
960 bool QNetworkReplyImpl::canReadLine () const
961 {
962     Q_D(const QNetworkReplyImpl);
963     return QNetworkReply::canReadLine() || d->readBuffer.canReadLine();
964 }
965
966
967 /*!
968     Returns the number of bytes available for reading with
969     QIODevice::read(). The number of bytes available may grow until
970     the finished() signal is emitted.
971 */
972 qint64 QNetworkReplyImpl::bytesAvailable() const
973 {
974     // Special case for the "zero copy" download buffer
975     Q_D(const QNetworkReplyImpl);
976     if (d->downloadBuffer) {
977         qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
978         return QNetworkReply::bytesAvailable() + maxAvail;
979     }
980
981     return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
982 }
983
984 void QNetworkReplyImpl::setReadBufferSize(qint64 size)
985 {
986     Q_D(QNetworkReplyImpl);
987     if (size > d->readBufferMaxSize &&
988         size > d->readBuffer.byteAmount())
989         d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
990
991     QNetworkReply::setReadBufferSize(size);
992
993     if (d->backend)
994         d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
995 }
996
997 #ifndef QT_NO_SSL
998 void QNetworkReplyImpl::sslConfigurationImplementation(QSslConfiguration &configuration) const
999 {
1000     Q_D(const QNetworkReplyImpl);
1001     if (d->backend)
1002         d->backend->fetchSslConfiguration(configuration);
1003 }
1004
1005 void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
1006 {
1007     Q_D(QNetworkReplyImpl);
1008     if (d->backend && !config.isNull())
1009         d->backend->setSslConfiguration(config);
1010 }
1011
1012 void QNetworkReplyImpl::ignoreSslErrors()
1013 {
1014     Q_D(QNetworkReplyImpl);
1015     if (d->backend)
1016         d->backend->ignoreSslErrors();
1017 }
1018
1019 void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
1020 {
1021     Q_D(QNetworkReplyImpl);
1022     if (d->backend)
1023         d->backend->ignoreSslErrors(errors);
1024 }
1025 #endif  // QT_NO_SSL
1026
1027 /*!
1028     \internal
1029 */
1030 qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
1031 {
1032     Q_D(QNetworkReplyImpl);
1033
1034     // Special case code if we have the "zero copy" download buffer
1035     if (d->downloadBuffer) {
1036         qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
1037         if (maxAvail == 0)
1038             return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
1039         // FIXME what about "Aborted" state?
1040         memcpy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
1041         d->downloadBufferReadPosition += maxAvail;
1042         return maxAvail;
1043     }
1044
1045
1046     if (d->readBuffer.isEmpty())
1047         return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
1048     // FIXME what about "Aborted" state?
1049
1050     d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
1051     if (maxlen == 1) {
1052         // optimization for getChar()
1053         *data = d->readBuffer.getChar();
1054         return 1;
1055     }
1056
1057     maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
1058     return d->readBuffer.read(data, maxlen);
1059 }
1060
1061 /*!
1062    \internal Reimplemented for internal purposes
1063 */
1064 bool QNetworkReplyImpl::event(QEvent *e)
1065 {
1066     if (e->type() == QEvent::NetworkReplyUpdated) {
1067         d_func()->handleNotifications();
1068         return true;
1069     }
1070
1071     return QObject::event(e);
1072 }
1073
1074 /*
1075     Migrates the backend of the QNetworkReply to a new network connection if required.  Returns
1076     true if the reply is migrated or it is not required; otherwise returns false.
1077 */
1078 bool QNetworkReplyImplPrivate::migrateBackend()
1079 {
1080     Q_Q(QNetworkReplyImpl);
1081
1082     // Network reply is already finished or aborted, don't need to migrate.
1083     if (state == Finished || state == Aborted)
1084         return true;
1085
1086     // Request has outgoing data, not migrating.
1087     if (outgoingData)
1088         return false;
1089
1090     // Request is serviced from the cache, don't need to migrate.
1091     if (copyDevice)
1092         return true;
1093
1094     // Backend does not support resuming download.
1095     if (!backend->canResume())
1096         return false;
1097
1098     state = QNetworkReplyImplPrivate::Reconnecting;
1099
1100     if (backend) {
1101         delete backend;
1102         backend = 0;
1103     }
1104
1105     cookedHeaders.clear();
1106     rawHeaders.clear();
1107
1108     preMigrationDownloaded = bytesDownloaded;
1109
1110     backend = manager->d_func()->findBackend(operation, request);
1111
1112     if (backend) {
1113         backend->setParent(q);
1114         backend->reply = this;
1115         backend->setResumeOffset(bytesDownloaded);
1116     }
1117
1118 #ifndef QT_NO_HTTP
1119     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1120 #else
1121     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1122 #endif // QT_NO_HTTP
1123
1124     return true;
1125 }
1126
1127 #ifndef QT_NO_BEARERMANAGEMENT
1128 QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent,
1129                                              const QNetworkRequest &req,
1130                                              QNetworkAccessManager::Operation op)
1131 :   QNetworkReply(parent)
1132 {
1133     setRequest(req);
1134     setUrl(req.url());
1135     setOperation(op);
1136
1137     qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
1138
1139     QString msg = QCoreApplication::translate("QNetworkAccessManager",
1140                                               "Network access is disabled.");
1141     setError(UnknownNetworkError, msg);
1142
1143     QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
1144         Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
1145     QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
1146 }
1147
1148 QDisabledNetworkReply::~QDisabledNetworkReply()
1149 {
1150 }
1151 #endif
1152
1153 QT_END_NAMESPACE
1154
1155 #include "moc_qnetworkreplyimpl_p.cpp"
1156