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