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