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