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