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