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