Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qnetworkreplyimpl.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
10#include "QtCore/qcoreapplication.h"
11#include "QtCore/qdatetime.h"
12#include "QtNetwork/qsslconfiguration.h"
14
15#include <QtCore/QCoreApplication>
16
18
19QT_IMPL_METATYPE_EXTERN_TAGGED(QSharedPointer<char>, QSharedPointer_char)
20
21inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
22 : backend(nullptr), outgoingData(nullptr),
23 copyDevice(nullptr),
24 cacheEnabled(false), cacheSaveDevice(nullptr),
25 notificationHandlingPaused(false),
26 bytesDownloaded(0), bytesUploaded(-1),
27 httpStatusCode(0),
28 state(Idle)
29 , downloadBufferReadPosition(0)
30 , downloadBufferCurrentSize(0)
31 , downloadBufferMaximumSize(0)
32 , downloadBuffer(nullptr)
33{
34 if (request.attribute(QNetworkRequest::EmitAllUploadProgressSignalsAttribute).toBool() == true)
35 emitAllUploadProgressSignals = true;
36}
37
39{
40 // ensure this function is only being called once
41 if (state == Working || state == Finished) {
42 qDebug() << "QNetworkReplyImpl::_q_startOperation was called more than once" << url;
43 return;
44 }
45 state = Working;
46
47 // note: if that method is called directly, it cannot happen that the backend is 0,
48 // because we just checked via a qobject_cast that we got a http backend (see
49 // QNetworkReplyImplPrivate::setup())
50 if (!backend) {
51 error(QNetworkReplyImpl::ProtocolUnknownError,
52 QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;
54 return;
55 }
56
57 if (!backend->start()) {
58 qWarning("Backend start failed");
59 state = Working;
60 error(QNetworkReplyImpl::UnknownNetworkError,
61 QCoreApplication::translate("QNetworkReply", "backend start error."));
63 return;
64 }
65
66 // Prepare timer for progress notifications
67 downloadProgressSignalChoke.start();
68 uploadProgressSignalChoke.invalidate();
69
70 if (backend && backend->isSynchronous()) {
71 state = Finished;
72 q_func()->setFinished(true);
73 } else {
74 if (state != Finished) {
75 if (operation == QNetworkAccessManager::GetOperation)
76 pendingNotifications.push_back(NotifyDownstreamReadyWrite);
77
79 }
80 }
81}
82
84{
85 Q_Q(QNetworkReplyImpl);
86 if (state != Working)
87 return;
88 if (!copyDevice || !q->isOpen())
89 return;
90
91 // FIXME Optimize to use download buffer if it is a QBuffer.
92 // Needs to be done where sendCacheContents() (?) of HTTP is emitting
93 // metaDataChanged ?
94 qint64 lastBytesDownloaded = bytesDownloaded;
95 forever {
96 qint64 bytesToRead = nextDownstreamBlockSize();
97 if (bytesToRead == 0)
98 // we'll be called again, eventually
99 break;
100
101 bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
102 qint64 bytesActuallyRead = copyDevice->read(buffer.reserve(bytesToRead), bytesToRead);
103 if (bytesActuallyRead == -1) {
104 buffer.chop(bytesToRead);
105 break;
106 }
107 buffer.chop(bytesToRead - bytesActuallyRead);
108
109 if (!copyDevice->isSequential() && copyDevice->atEnd()) {
110 bytesDownloaded += bytesActuallyRead;
111 break;
112 }
113
114 bytesDownloaded += bytesActuallyRead;
115 }
116
117 if (bytesDownloaded == lastBytesDownloaded) {
118 // we didn't read anything
119 return;
120 }
121
122 const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
123 headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
124
126 // emit readyRead before downloadProgress in case this will cause events to be
127 // processed and we get into a recursive call (as in QProgressDialog).
128 emit q->readyRead();
129 if (downloadProgressSignalChoke.isValid() &&
130 downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
131 downloadProgressSignalChoke.start();
132 emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
133 }
135}
136
141
143{
144 Q_Q(QNetworkReplyImpl);
145
146 // make sure this is only called once, ever.
147 //_q_bufferOutgoingData may call it or the readChannelFinished emission
148 if (state != Buffering)
149 return;
150
151 // disconnect signals
152 QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
153 QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
154
155 // finally, start the request
156 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
157}
158
160{
161 Q_Q(QNetworkReplyImpl);
162
163 if (!outgoingDataBuffer) {
164 // first call, create our buffer
165 outgoingDataBuffer = std::make_shared<QRingBuffer>();
166
167 QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
168 QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
169 }
170
171 qint64 bytesBuffered = 0;
172 qint64 bytesToBuffer = 0;
173
174 // read data into our buffer
175 forever {
176 bytesToBuffer = outgoingData->bytesAvailable();
177 // unknown? just try 2 kB, this also ensures we always try to read the EOF
178 if (bytesToBuffer <= 0)
179 bytesToBuffer = 2*1024;
180
181 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
182 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
183
184 if (bytesBuffered == -1) {
185 // EOF has been reached.
186 outgoingDataBuffer->chop(bytesToBuffer);
187
188 _q_bufferOutgoingDataFinished();
189 break;
190 } else if (bytesBuffered == 0) {
191 // nothing read right now, just wait until we get called again
192 outgoingDataBuffer->chop(bytesToBuffer);
193
194 break;
195 } else {
196 // don't break, try to read() again
197 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
198 }
199 }
200}
201
202void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
203 QIODevice *data)
204{
205 Q_Q(QNetworkReplyImpl);
206
207 outgoingData = data;
208 request = req;
209 originalRequest = req;
210 url = request.url();
211 operation = op;
212
213 q->QIODevice::open(QIODevice::ReadOnly);
214 // Internal code that does a HTTP reply for the synchronous Ajax
215 // in Qt WebKit.
216 QVariant synchronousHttpAttribute = req.attribute(
217 static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
218 // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
219 // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
220 if (synchronousHttpAttribute.toBool() && outgoingData) {
221 outgoingDataBuffer = std::make_shared<QRingBuffer>();
222 qint64 previousDataSize = 0;
223 do {
224 previousDataSize = outgoingDataBuffer->size();
225 outgoingDataBuffer->append(outgoingData->readAll());
226 } while (outgoingDataBuffer->size() != previousDataSize);
227 }
228
229 if (backend)
230 backend->setSynchronous(synchronousHttpAttribute.toBool());
231
232
233 if (outgoingData && backend && !backend->isSynchronous()) {
234 // there is data to be uploaded, e.g. HTTP POST.
235
236 if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
237 // backend does not need upload buffering or
238 // fixed size non-sequential
239 // just start the operation
240 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
241 } else {
242 bool bufferingDisallowed =
243 req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
244 false).toBool();
245
246 if (bufferingDisallowed) {
247 // if a valid content-length header for the request was supplied, we can disable buffering
248 // if not, we will buffer anyway
249 const auto sizeOpt = QNetworkHeadersPrivate::toInt(
250 headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
251
252 if (sizeOpt) {
253 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
254 } else {
255 state = Buffering;
256 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
257 }
258 } else {
259 // _q_startOperation will be called when the buffering has finished.
260 state = Buffering;
261 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
262 }
263 }
264 } else {
265 // for HTTP, we want to send out the request as fast as possible to the network, without
266 // invoking methods in a QueuedConnection
267 if (backend && backend->isSynchronous())
269 else
270 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
271 }
272}
273
275{
276 Q_Q(QNetworkReplyImpl);
277 const auto it = std::find(pendingNotifications.cbegin(), pendingNotifications.cend(), notification);
278 if (it == pendingNotifications.cend())
279 pendingNotifications.push_back(notification);
280
281 if (pendingNotifications.size() == 1)
282 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
283}
284
286{
288 return;
289
290 for (InternalNotifications notification : std::exchange(pendingNotifications, {})) {
291 if (state != Working)
292 return;
293 switch (notification) {
294 case NotifyDownstreamReadyWrite:
295 if (copyDevice) {
296 _q_copyReadyRead();
297 } else if (backend) {
298 if (backend->bytesAvailable() > 0)
299 readFromBackend();
300 else if (backend->wantToRead())
301 readFromBackend();
302 }
303 break;
304 }
305 }
306}
307
308// Do not handle the notifications while we are emitting downloadProgress
309// or readyRead
314
315// Resume notification handling
317{
318 Q_Q(QNetworkReplyImpl);
320 if (pendingNotifications.size() >= 1)
321 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
322}
323
324QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
325{
326 if (!backend)
327 return nullptr;
328 return backend->networkCache();
329}
330
332{
333 // check if we can save and if we're allowed to
334 if (!networkCache()
335 || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool())
336 return;
337 cacheEnabled = true;
338}
339
341{
342 return (cacheEnabled && networkCache() != nullptr);
343}
344
346{
347 if (!enable && !cacheEnabled)
348 return; // nothing to do
349 if (enable && cacheEnabled)
350 return; // nothing to do either!
351
352 if (enable) {
353 if (Q_UNLIKELY(bytesDownloaded)) {
354 // refuse to enable in this case
355 qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
356 return;
357 }
358
360 } else {
361 // someone told us to turn on, then back off?
362 // ok... but you should make up your mind
363 qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
364 "backend %s probably needs to be fixed",
365 backend->metaObject()->className());
366 networkCache()->remove(url);
367 cacheSaveDevice = nullptr;
368 cacheEnabled = false;
369 }
370}
371
373{
374 if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
375 networkCache()->remove(url);
376 } else if (cacheEnabled && cacheSaveDevice) {
377 networkCache()->insert(cacheSaveDevice);
378 }
379 cacheSaveDevice = nullptr;
380 cacheEnabled = false;
381}
382
383void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
384{
385 Q_Q(QNetworkReplyImpl);
386 bytesUploaded = bytesSent;
387
388 if (!emitAllUploadProgressSignals) {
389 //choke signal emissions, except the first and last signals which are unconditional
390 if (uploadProgressSignalChoke.isValid()) {
391 if (bytesSent != bytesTotal && uploadProgressSignalChoke.elapsed() < progressSignalInterval) {
392 return;
393 }
394 }
395 uploadProgressSignalChoke.start();
396 }
397
399 emit q->uploadProgress(bytesSent, bytesTotal);
401}
402
403
405{
406 enum { DesiredBufferSize = 32 * 1024 };
407 if (readBufferMaxSize == 0)
408 return DesiredBufferSize;
409
410 return qMax<qint64>(0, readBufferMaxSize - buffer.size());
411}
412
414{
415 Q_Q(QNetworkReplyImpl);
416
417 // The disk cache does not support partial content, so don't even try to
418 // save any such content into the cache.
419 if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
420 cacheEnabled = false;
421 return;
422 }
423
424 // save the meta data
425 QNetworkCacheMetaData metaData;
426 metaData.setUrl(url);
427 // @todo @future: fetchCacheMetaData is not currently implemented in any backend, but can be useful again in the future
428 // metaData = backend->fetchCacheMetaData(metaData);
429
430 // save the redirect request also in the cache
431 QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
432 if (redirectionTarget.isValid()) {
433 QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
434 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
435 metaData.setAttributes(attributes);
436 }
437
438 cacheSaveDevice = networkCache()->prepare(metaData);
439
440 if (!cacheSaveDevice || !cacheSaveDevice->isOpen()) {
441 if (Q_UNLIKELY(cacheSaveDevice && !cacheSaveDevice->isOpen()))
442 qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
443 "class %s probably needs to be fixed",
444 networkCache()->metaObject()->className());
445
446 networkCache()->remove(url);
447 cacheSaveDevice = nullptr;
448 cacheEnabled = false;
449 }
450}
451
452// we received downstream data and send this to the cache
453// and to our buffer (which in turn gets read by the user of QNetworkReply)
455{
456 Q_Q(QNetworkReplyImpl);
457 if (!q->isOpen())
458 return;
459
460 if (cacheEnabled && !cacheSaveDevice) {
462 }
463
464 qint64 bytesWritten = 0;
465 for (qsizetype i = 0; i < data.bufferCount(); ++i) {
466 QByteArray const &item = data[i];
467
468 if (cacheSaveDevice)
469 cacheSaveDevice->write(item.constData(), item.size());
470 buffer.append(item);
471
472 bytesWritten += item.size();
473 }
474 data.clear();
475
476 bytesDownloaded += bytesWritten;
477
479}
480
482{
483 Q_Q(QNetworkReplyImpl);
484
485 const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
486 headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
488 // important: At the point of this readyRead(), the data parameter list must be empty,
489 // else implicit sharing will trigger memcpy when the user is reading data!
490 emit q->readyRead();
491 // emit readyRead before downloadProgress in case this will cause events to be
492 // processed and we get into a recursive call (as in QProgressDialog).
493 if (downloadProgressSignalChoke.isValid() &&
494 downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
495 downloadProgressSignalChoke.start();
496 emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
497 }
498
500 // do we still have room in the buffer?
501 if (nextDownstreamBlockSize() > 0)
503}
504
505// this is used when it was fetched from the cache, right?
506void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
507{
508 Q_Q(QNetworkReplyImpl);
509 if (!q->isOpen())
510 return;
511
512 // read until EOF from data
513 if (Q_UNLIKELY(copyDevice)) {
514 qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
515 "backend probably needs to be fixed");
516 return;
517 }
518
519 copyDevice = data;
520 q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
521 q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
522
523 // start the copy:
525}
526
528{
529 Q_Q(QNetworkReplyImpl);
530
531 if (!downloadBuffer) {
532 // We are requested to create it
533 // Check attribute() if allocating a buffer of that size can be allowed
534 QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
535 if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
536 downloadBufferCurrentSize = 0;
537 downloadBufferMaximumSize = size;
538 downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
539 downloadBufferPointer = QSharedPointer<char>(downloadBuffer, [](auto p) { delete[] p; });
540
541 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
542 }
543 }
544
545 return downloadBuffer;
546}
547
548void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
549{
550 Q_Q(QNetworkReplyImpl);
551
552 downloadBufferPointer = sp;
553 downloadBuffer = downloadBufferPointer.data();
554 downloadBufferCurrentSize = 0;
555 downloadBufferMaximumSize = size;
556 q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
557}
558
559
560void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
561{
562 Q_Q(QNetworkReplyImpl);
563 if (!q->isOpen())
564 return;
565
566 if (cacheEnabled && !cacheSaveDevice)
568
569 if (cacheSaveDevice && bytesReceived == bytesTotal) {
570 // Write everything in one go if we use a download buffer. might be more performant.
571 cacheSaveDevice->write(downloadBuffer, bytesTotal);
572 }
573
574 bytesDownloaded = bytesReceived;
575
576 downloadBufferCurrentSize = bytesReceived;
577
578 // Only emit readyRead when actual data is there
579 // emit readyRead before downloadProgress in case this will cause events to be
580 // processed and we get into a recursive call (as in QProgressDialog).
581 if (bytesDownloaded > 0)
582 emit q->readyRead();
583 if (downloadProgressSignalChoke.isValid() &&
584 downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
585 downloadProgressSignalChoke.start();
586 emit q->downloadProgress(bytesDownloaded, bytesTotal);
587 }
588}
589
591{
592 Q_Q(QNetworkReplyImpl);
593
594 if (state == Finished || state == Aborted)
595 return;
596
598 const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
599 headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
600 const auto totalSize = totalSizeOpt.value_or(-1);
601
603
604 state = Finished;
605 q->setFinished(true);
606
607 pendingNotifications.clear();
608
610 if (totalSize == -1) {
611 emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
612 } else {
613 emit q->downloadProgress(bytesDownloaded, totalSize);
614 }
615
616 if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
617 emit q->uploadProgress(0, 0);
619
620 // if we don't know the total size of or we received everything save the cache
621 if (totalSize == -1 || bytesDownloaded == totalSize)
623
624 // note: might not be a good idea, since users could decide to delete us
625 // which would delete the backend too...
626 // maybe we should protect the backend
628 emit q->readChannelFinished();
629 emit q->finished();
631}
632
633void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
634{
635 Q_Q(QNetworkReplyImpl);
636 // Can't set and emit multiple errors.
637 if (errorCode != QNetworkReply::NoError) {
638 qWarning( "QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
639 return;
640 }
641
642 errorCode = code;
643 q->setErrorString(errorMessage);
644
645 // note: might not be a good idea, since users could decide to delete us
646 // which would delete the backend too...
647 // maybe we should protect the backend
648 emit q->errorOccurred(code);
649}
650
652{
653 Q_Q(QNetworkReplyImpl);
654 // 1. do we have cookies?
655 // 2. are we allowed to set them?
656 if (!manager.isNull()) {
657 const auto cookiesOpt = QNetworkHeadersPrivate::toSetCookieList(
658 headers().values(QHttpHeaders::WellKnownHeader::SetCookie));
659 const auto cookies = cookiesOpt.value_or(QList<QNetworkCookie>());
660 if (!cookies.empty()
661 && request.attribute(QNetworkRequest::CookieSaveControlAttribute,
662 QNetworkRequest::Automatic).toInt() == QNetworkRequest::Automatic) {
663 QNetworkCookieJar *jar = manager->cookieJar();
664 if (jar) {
665 jar->setCookiesFromUrl(cookies, url);
666 }
667 }
668 }
669
670 emit q->metaDataChanged();
671}
672
674{
675 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
676}
677
679{
680#ifndef QT_NO_SSL
681 Q_Q(QNetworkReplyImpl);
682 emit q->encrypted();
683#endif
684}
685
686void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
687{
688#ifndef QT_NO_SSL
689 Q_Q(QNetworkReplyImpl);
690 emit q->sslErrors(errors);
691#else
692 Q_UNUSED(errors);
693#endif
694}
695
697{
698 Q_Q(QNetworkReplyImpl);
699 if (!backend)
700 return;
701
702 if (backend->ioFeatures() & QNetworkAccessBackend::IOFeature::ZeroCopy) {
703 if (backend->bytesAvailable())
704 emit q->readyRead();
705 } else {
706 bool anyBytesRead = false;
707 while (backend->bytesAvailable()
708 && (!readBufferMaxSize || buffer.size() < readBufferMaxSize)) {
709 qint64 toRead = qMin(nextDownstreamBlockSize(), backend->bytesAvailable());
710 if (toRead == 0)
711 toRead = 16 * 1024; // try to read something
712 char *data = buffer.reserve(toRead);
713 qint64 bytesRead = backend->read(data, toRead);
714 Q_ASSERT(bytesRead <= toRead);
715 buffer.chop(toRead - bytesRead);
716 anyBytesRead |= bytesRead > 0;
717 }
718 if (anyBytesRead)
719 emit q->readyRead();
720 }
721}
722
723QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
724 : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
725{
726}
727
729{
730 Q_D(QNetworkReplyImpl);
731
732 // This code removes the data from the cache if it was prematurely aborted.
733 // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
734 // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
735 if (d->isCachingEnabled())
736 d->networkCache()->remove(url());
737}
738
740{
741 Q_D(QNetworkReplyImpl);
742 if (d->state == QNetworkReplyPrivate::Finished || d->state == QNetworkReplyPrivate::Aborted)
743 return;
744
745 // stop both upload and download
746 if (d->outgoingData)
747 disconnect(d->outgoingData, nullptr, this, nullptr);
748 if (d->copyDevice)
749 disconnect(d->copyDevice, nullptr, this, nullptr);
750
751 QNetworkReply::close();
752
753 // call finished which will emit signals
754 d->error(OperationCanceledError, tr("Operation canceled"));
755 d->finished();
756 d->state = QNetworkReplyPrivate::Aborted;
757
758 // finished may access the backend
759 if (d->backend) {
760 d->backend->deleteLater();
761 d->backend = nullptr;
762 }
763}
764
766{
767 Q_D(QNetworkReplyImpl);
768 if (d->state == QNetworkReplyPrivate::Aborted ||
769 d->state == QNetworkReplyPrivate::Finished)
770 return;
771
772 // stop the download
773 if (d->backend)
774 d->backend->close();
775 if (d->copyDevice)
776 disconnect(d->copyDevice, nullptr, this, nullptr);
777
778 QNetworkReply::close();
779
780 // call finished which will emit signals
781 d->error(OperationCanceledError, tr("Operation canceled"));
782 d->finished();
783}
784
785/*!
786 \class QNetworkReplyImpl
787 \internal
788*/
789
790/*!
791 Returns the number of bytes available for reading with
792 QIODevice::read(). The number of bytes available may grow until
793 the finished() signal is emitted.
794*/
796{
797 // Special case for the "zero copy" download buffer
798 Q_D(const QNetworkReplyImpl);
799 if (d->downloadBuffer) {
800 qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
801 return QNetworkReply::bytesAvailable() + maxAvail;
802 }
803 return QNetworkReply::bytesAvailable() + (d->backend ? d->backend->bytesAvailable() : 0);
804}
805
807{
808 Q_D(QNetworkReplyImpl);
809 qint64 oldMaxSize = d->readBufferMaxSize;
810 QNetworkReply::setReadBufferSize(size);
811 if (size > oldMaxSize && size > d->buffer.size())
812 d->readFromBackend();
813}
814
815#ifndef QT_NO_SSL
816void QNetworkReplyImpl::sslConfigurationImplementation(QSslConfiguration &configuration) const
817{
818 Q_D(const QNetworkReplyImpl);
819 if (d->backend)
820 configuration = d->backend->sslConfiguration();
821}
822
823void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
824{
825 Q_D(QNetworkReplyImpl);
826 if (d->backend && !config.isNull())
827 d->backend->setSslConfiguration(config);
828}
829
831{
832 Q_D(QNetworkReplyImpl);
833 if (d->backend)
834 d->backend->ignoreSslErrors();
835}
836
837void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
838{
839 Q_D(QNetworkReplyImpl);
840 if (d->backend)
841 d->backend->ignoreSslErrors(errors);
842}
843#endif // QT_NO_SSL
844
845/*!
846 \internal
847*/
848qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
849{
850 Q_D(QNetworkReplyImpl);
851
852 if (d->backend
853 && d->backend->ioFeatures().testFlag(QNetworkAccessBackend::IOFeature::ZeroCopy)) {
854 qint64 bytesRead = 0;
855 while (d->backend->bytesAvailable()) {
856 QByteArrayView view = d->backend->readPointer();
857 if (view.size()) {
858 qint64 bytesToCopy = qMin(qint64(view.size()), maxlen - bytesRead);
859 memcpy(data + bytesRead, view.data(), bytesToCopy); // from zero to one copy
860
861 // We might have to cache this
862 if (d->cacheEnabled && !d->cacheSaveDevice)
863 d->initCacheSaveDevice();
864 if (d->cacheEnabled && d->cacheSaveDevice)
865 d->cacheSaveDevice->write(view.data(), view.size());
866
867 bytesRead += bytesToCopy;
868 d->backend->advanceReadPointer(bytesToCopy);
869 } else {
870 break;
871 }
872 }
873
874 const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
875 headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
876 emit downloadProgress(bytesRead, totalSizeOpt.value_or(-1));
877 return bytesRead;
878 } else if (d->backend && d->backend->bytesAvailable()) {
879 return d->backend->read(data, maxlen);
880 }
881
882 // Special case code if we have the "zero copy" download buffer
883 if (d->downloadBuffer) {
884 qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
885 if (maxAvail == 0)
886 return d->state == QNetworkReplyPrivate::Finished ? -1 : 0;
887 // FIXME what about "Aborted" state?
888 memcpy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
889 d->downloadBufferReadPosition += maxAvail;
890 return maxAvail;
891 }
892
893
894 // FIXME what about "Aborted" state?
895 if (d->state == QNetworkReplyPrivate::Finished)
896 return -1;
897
898 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
899 return 0;
900}
901
902/*!
903 \internal Reimplemented for internal purposes
904*/
905bool QNetworkReplyImpl::event(QEvent *e)
906{
907 if (e->type() == QEvent::NetworkReplyUpdated) {
908 d_func()->handleNotifications();
909 return true;
910 }
911
912 return QObject::event(e);
913}
914
915QT_END_NAMESPACE
916
917#include "moc_qnetworkreplyimpl_p.cpp"
The QNetworkCacheMetaData class provides cache information.
void setDownloadBuffer(QSharedPointer< char > sp, qint64 size)
void appendDownstreamDataDownloadBuffer(qint64, qint64)
QNetworkAccessBackend * backend
void emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
void backendNotify(InternalNotifications notification)
QAbstractNetworkCache * networkCache() const
void appendDownstreamData(QByteDataBuffer &data)
char * getDownloadBuffer(qint64 size)
void sslErrors(const QList< QSslError > &errors)
void redirectionRequested(const QUrl &target)
void setCachingEnabled(bool enable)
virtual qint64 bytesAvailable() const override
Returns the number of bytes available for reading with QIODevice::read().
virtual qint64 readData(char *data, qint64 maxlen) override
virtual void ignoreSslErrorsImplementation(const QList< QSslError > &errors) override
virtual void setReadBufferSize(qint64 size) override
Sets the size of the read buffer to be size bytes.
virtual void close() override
Closes this device for reading.
virtual void abort() override
Aborts the operation immediately and closes any network connections still open.
virtual bool event(QEvent *) override
void void void void _q_bufferOutgoingDataFinished()) protected void setSslConfigurationImplementation(const QSslConfiguration &configuration) override
virtual void ignoreSslErrors() override
If this function is called, SSL errors related to network connection will be ignored,...