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