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