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