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