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