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