summaryrefslogtreecommitdiffstats
path: root/src/network/access
diff options
context:
space:
mode:
authorMårten Nordheim <marten.nordheim@qt.io>2020-05-13 11:04:28 +0200
committerMårten Nordheim <marten.nordheim@qt.io>2021-06-12 00:04:04 +0200
commit6f25051536c1636688a0a0939196007aac34676d (patch)
tree390fc3fc4e0147a51d56aee6d563614b77981b6a /src/network/access
parentf9b867216ba2728ff993020599f5062e2f023de1 (diff)
QNetworkReply: Decompress when reading
Rather than when the data is received. Source compatibility is achieved through double-decompressing the data. This lets us know how many bytes are available just as before but without having the uncompressed data left in memory. Fixes: QTBUG-83269 Change-Id: I352bd09581614c582e4628243e2a0e895ba4946b Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src/network/access')
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp24
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp1
-rw-r--r--src/network/access/qhttpnetworkreply.cpp38
-rw-r--r--src/network/access/qhttpnetworkreply_p.h2
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp13
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h4
-rw-r--r--src/network/access/qhttpprotocolhandler.cpp3
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp5
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h4
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp174
-rw-r--r--src/network/access/qnetworkreplyhttpimpl_p.h9
11 files changed, 155 insertions, 122 deletions
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index aaa7b58ac4..3b28775466 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -1233,13 +1233,8 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
if (QHttpNetworkReply::isHttpRedirect(statusCode) && redirectUrl.isValid())
httpReply->setRedirectUrl(redirectUrl);
- if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress) {
+ if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress)
httpReplyPrivate->removeAutoDecompressHeader();
- httpReplyPrivate->decompressHelper.setEncoding(
- httpReplyPrivate->headerField("content-encoding"));
- httpReplyPrivate->decompressHelper.setMinimumArchiveBombSize(
- httpReplyPrivate->request.minimumArchiveBombSize());
- }
if (QHttpNetworkReply::isHttpRedirect(statusCode)) {
// Note: This status code can trigger uploadByteDevice->reset() in
@@ -1276,27 +1271,12 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const Frame &frame,
if (const auto length = frame.dataSize()) {
const char *data = reinterpret_cast<const char *>(frame.dataBegin());
- auto &httpRequest = stream.request();
auto replyPrivate = httpReply->d_func();
replyPrivate->totalProgress += length;
const QByteArray wrapped(data, length);
- if (httpRequest.d->autoDecompress && replyPrivate->isCompressed()) {
- Q_ASSERT(replyPrivate->decompressHelper.isValid());
-
- replyPrivate->decompressHelper.feed(wrapped);
- while (replyPrivate->decompressHelper.hasData()) {
- QByteArray output(4 * 1024, Qt::Uninitialized);
- qint64 read = replyPrivate->decompressHelper.read(output.data(), output.size());
- if (read > 0) {
- output.resize(read);
- replyPrivate->responseData.append(std::move(output));
- }
- }
- } else {
- replyPrivate->responseData.append(wrapped);
- }
+ replyPrivate->responseData.append(wrapped);
if (replyPrivate->shouldEmitSignals()) {
if (connectionType == Qt::DirectConnection) {
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 18134dda13..dec354b47f 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -48,6 +48,7 @@
#include <qnetworkproxy.h>
#include <qauthenticator.h>
#include <qcoreapplication.h>
+#include <private/qdecompresshelper_p.h>
#include <qbuffer.h>
#include <qpair.h>
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index a9579d71bc..0ad5ee372e 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -46,6 +46,8 @@
# include <QtNetwork/qsslconfiguration.h>
#endif
+#include <private/qdecompresshelper_p.h>
+
QT_BEGIN_NAMESPACE
QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
@@ -348,7 +350,6 @@ void QHttpNetworkReplyPrivate::clearHttpLayerInformation()
currentChunkRead = 0;
lastChunkRead = false;
connectionCloseEnabled = true;
- decompressHelper.clear();
fields.clear();
}
@@ -554,11 +555,6 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
headerField("proxy-connection").toLower().contains("close")) ||
(majorVersion == 1 && minorVersion == 0 &&
(connectionHeaderField.isEmpty() && !headerField("proxy-connection").toLower().contains("keep-alive")));
- if (autoDecompress && isCompressed()) {
- if (!decompressHelper.setEncoding(headerField("content-encoding")))
- return -1; // Either the encoding was unsupported or the decoder could not be set up
- decompressHelper.setMinimumArchiveBombSize(request.minimumArchiveBombSize());
- }
}
return bytes;
}
@@ -660,42 +656,18 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuff
{
qint64 bytes = 0;
- // for compressed data we'll allocate a temporary one that we then decompress
- QByteDataBuffer *tempOutDataBuffer = (autoDecompress ? new QByteDataBuffer : out);
-
-
if (isChunked()) {
// chunked transfer encoding (rfc 2616, sec 3.6)
- bytes += readReplyBodyChunked(socket, tempOutDataBuffer);
+ bytes += readReplyBodyChunked(socket, out);
} else if (bodyLength > 0) {
// we have a Content-Length
- bytes += readReplyBodyRaw(socket, tempOutDataBuffer, bodyLength - contentRead);
+ bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead);
if (contentRead + bytes == bodyLength)
state = AllDoneState;
} else {
// no content length. just read what's possible
- bytes += readReplyBodyRaw(socket, tempOutDataBuffer, socket->bytesAvailable());
- }
-
- // This is true if there is compressed encoding and we're supposed to use it.
- if (autoDecompress) {
- QScopedPointer holder(tempOutDataBuffer);
- if (!decompressHelper.isValid())
- return -1;
-
- decompressHelper.feed(std::move(*tempOutDataBuffer));
- while (decompressHelper.hasData()) {
- QByteArray output(4 * 1024, Qt::Uninitialized);
- qint64 read = decompressHelper.read(output.data(), output.size());
- if (read < 0) {
- return -1;
- } else if (read > 0) {
- output.resize(read);
- out->append(std::move(output));
- }
- }
+ bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable());
}
-
contentRead += bytes;
return bytes;
}
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index 1fb965f989..618de43b14 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -274,8 +274,6 @@ public:
char* userProvidedDownloadBuffer;
QUrl redirectUrl;
-
- QDecompressHelper decompressHelper;
};
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index c1b859958a..e3c699efed 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -57,7 +57,6 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
customVerb(other.customVerb),
priority(other.priority),
uploadByteDevice(other.uploadByteDevice),
- minimumArchiveBombSize(other.minimumArchiveBombSize),
autoDecompress(other.autoDecompress),
pipeliningAllowed(other.pipeliningAllowed),
http2Allowed(other.http2Allowed),
@@ -94,7 +93,7 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
&& (redirectPolicy == other.redirectPolicy)
&& (peerVerifyName == other.peerVerifyName)
&& (needResendWithCredentials == other.needResendWithCredentials)
- && (minimumArchiveBombSize == other.minimumArchiveBombSize);
+ ;
}
QByteArray QHttpNetworkRequest::methodName() const
@@ -406,15 +405,5 @@ void QHttpNetworkRequest::setPeerVerifyName(const QString &peerName)
d->peerVerifyName = peerName;
}
-qint64 QHttpNetworkRequest::minimumArchiveBombSize() const
-{
- return d->minimumArchiveBombSize;
-}
-
-void QHttpNetworkRequest::setMinimumArchiveBombSize(qint64 threshold)
-{
- d->minimumArchiveBombSize = threshold;
-}
-
QT_END_NAMESPACE
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index bcb9576474..50fdd64290 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -150,9 +150,6 @@ public:
QString peerVerifyName() const;
void setPeerVerifyName(const QString &peerName);
- qint64 minimumArchiveBombSize() const;
- void setMinimumArchiveBombSize(qint64 threshold);
-
private:
QSharedDataPointer<QHttpNetworkRequestPrivate> d;
friend class QHttpNetworkRequestPrivate;
@@ -178,7 +175,6 @@ public:
QByteArray customVerb;
QHttpNetworkRequest::Priority priority;
mutable QNonContiguousByteDevice* uploadByteDevice;
- qint64 minimumArchiveBombSize = 0;
bool autoDecompress;
bool pipeliningAllowed;
bool http2Allowed;
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
index c4ea73ce9d..f7f68ca65b 100644
--- a/src/network/access/qhttpprotocolhandler.cpp
+++ b/src/network/access/qhttpprotocolhandler.cpp
@@ -177,8 +177,7 @@ void QHttpProtocolHandler::_q_receiveReply()
m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::RemoteHostClosedError);
break;
}
- } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
- && replyPrivate->bodyLength > 0) {
+ } else if (!replyPrivate->isChunked() && replyPrivate->bodyLength > 0) {
// bulk files like images should fulfill these properties and
// we can therefore save on memory copying
qint64 haveRead = replyPrivate->readBodyFast(m_socket, &replyPrivate->responseData);
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index 05b720c4df..48e7953616 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -545,6 +545,7 @@ void QHttpThreadDelegate::synchronousFinishedSlot()
incomingErrorCode = statusCodeFromHttp(httpReply->statusCode(), httpRequest.url());
}
+ isCompressed = httpReply->isCompressed();
synchronousDownloadData = httpReply->readAll();
QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
@@ -634,6 +635,7 @@ void QHttpThreadDelegate::headerChangedSlot()
incomingContentLength = httpReply->contentLength();
removedContentLength = httpReply->removedContentLength();
isHttp2Used = httpReply->isHttp2Used();
+ isCompressed = httpReply->isCompressed();
emit downloadMetaData(incomingHeaders,
incomingStatusCode,
@@ -642,7 +644,8 @@ void QHttpThreadDelegate::headerChangedSlot()
downloadBuffer,
incomingContentLength,
removedContentLength,
- isHttp2Used);
+ isHttp2Used,
+ isCompressed);
}
void QHttpThreadDelegate::synchronousHeaderChangedSlot()
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index 5849d3427f..5c3857b515 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -119,6 +119,8 @@ public:
QString incomingErrorDetail;
QHttp2Configuration http2Parameters;
+ bool isCompressed;
+
protected:
// The zerocopy download buffer, if used:
QSharedPointer<char> downloadBuffer;
@@ -142,7 +144,7 @@ signals:
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *);
#endif
void downloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &, bool,
- QSharedPointer<char>, qint64, qint64, bool);
+ QSharedPointer<char>, qint64, qint64, bool, bool);
void downloadProgress(qint64, qint64);
void downloadData(const QByteArray &);
void error(QNetworkReply::NetworkError, const QString &);
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 88f8e62df1..cc6590f713 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -305,6 +305,13 @@ qint64 QNetworkReplyHttpImpl::bytesAvailable() const
return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
}
+ if (d->decompressHelper.isValid()) {
+ if (d->decompressHelper.isCountingBytes())
+ return QNetworkReply::bytesAvailable() + d->decompressHelper.uncompressedSize();
+ if (d->decompressHelper.hasData())
+ return QNetworkReply::bytesAvailable() + 1;
+ }
+
// normal buffer
return QNetworkReply::bytesAvailable();
}
@@ -345,6 +352,30 @@ qint64 QNetworkReplyHttpImpl::readData(char* data, qint64 maxlen)
}
+ if (d->decompressHelper.isValid() && (d->decompressHelper.hasData() || !isFinished())) {
+ if (maxlen == 0 || !d->decompressHelper.hasData())
+ return 0;
+ const qint64 bytesRead = d->decompressHelper.read(data, maxlen);
+ if (!d->decompressHelper.isValid()) {
+ // error occurred, error copied from QHttpNetworkConnectionPrivate::errorDetail
+ d->error(QNetworkReplyImpl::NetworkError::ProtocolFailure,
+ QCoreApplication::translate("QHttp", "Data corrupted"));
+ }
+ if (d->cacheSaveDevice) {
+ // Need to write to the cache now that we have the data
+ d->cacheSaveDevice->write(data, bytesRead);
+ // ... and if we've read everything then the cache can be closed.
+ if (isFinished() && !d->decompressHelper.hasData())
+ d->completeCacheSave();
+ }
+ // In case of buffer size restriction we need to emit that it has been emptied
+ qint64 wasBuffered = d->bytesBuffered;
+ d->bytesBuffered = 0;
+ if (readBufferSize())
+ emit readBufferFreed(wasBuffered);
+ return bytesRead;
+ }
+
// normal buffer
if (d->state == d->Finished || d->state == d->Aborted)
return -1;
@@ -774,7 +805,6 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
if (request.attribute(QNetworkRequest::EmitAllUploadProgressSignalsAttribute).toBool())
emitAllUploadProgressSignals = true;
- httpRequest.setMinimumArchiveBombSize(newHttpRequest.minimumArchiveBombSize());
httpRequest.setPeerVerifyName(newHttpRequest.peerVerifyName());
// Create the HTTP thread delegate
@@ -839,14 +869,8 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
QObject::connect(delegate, SIGNAL(downloadFinished()),
q, SLOT(replyFinished()),
Qt::QueuedConnection);
- QObject::connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,
- int, QString, bool,
- QSharedPointer<char>, qint64, qint64,
- bool)),
- q, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,
- int, QString, bool,
- QSharedPointer<char>, qint64, qint64, bool)),
- Qt::QueuedConnection);
+ connect(delegate, &QHttpThreadDelegate::downloadMetaData, this,
+ &QNetworkReplyHttpImplPrivate::replyDownloadMetaData, Qt::QueuedConnection);
QObject::connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
q, SLOT(replyDownloadProgressSlot(qint64,qint64)),
Qt::QueuedConnection);
@@ -946,30 +970,20 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
if (synchronous) {
emit q->startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
- if (delegate->incomingErrorCode != QNetworkReply::NoError) {
- replyDownloadMetaData
- (delegate->incomingHeaders,
- delegate->incomingStatusCode,
- delegate->incomingReasonPhrase,
- delegate->isPipeliningUsed,
- QSharedPointer<char>(),
- delegate->incomingContentLength,
- delegate->removedContentLength,
- delegate->isHttp2Used);
- replyDownloadData(delegate->synchronousDownloadData);
+ replyDownloadMetaData
+ (delegate->incomingHeaders,
+ delegate->incomingStatusCode,
+ delegate->incomingReasonPhrase,
+ delegate->isPipeliningUsed,
+ QSharedPointer<char>(),
+ delegate->incomingContentLength,
+ delegate->removedContentLength,
+ delegate->isHttp2Used,
+ delegate->isCompressed);
+ replyDownloadData(delegate->synchronousDownloadData);
+
+ if (delegate->incomingErrorCode != QNetworkReply::NoError)
httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
- } else {
- replyDownloadMetaData
- (delegate->incomingHeaders,
- delegate->incomingStatusCode,
- delegate->incomingReasonPhrase,
- delegate->isPipeliningUsed,
- QSharedPointer<char>(),
- delegate->incomingContentLength,
- delegate->removedContentLength,
- delegate->isHttp2Used);
- replyDownloadData(delegate->synchronousDownloadData);
- }
thread->quit();
thread->wait(QDeadlineTimer(5000));
@@ -1040,23 +1054,72 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
if (!q->isOpen())
return;
+ // cache this, we need it later and it's invalidated when dealing with compressed data
+ auto dataSize = d.size();
+ // Grab this to compare later (only relevant for compressed data) in case none of the data
+ // will be propagated to the user
+ const qint64 previousBytesDownloaded = bytesDownloaded;
+
if (cacheEnabled && isCachingAllowed() && !cacheSaveDevice)
initCacheSaveDevice();
+ if (decompressHelper.isValid()) {
+ qint64 uncompressedBefore = -1;
+ if (decompressHelper.isCountingBytes())
+ uncompressedBefore = decompressHelper.uncompressedSize();
+
+ decompressHelper.feed(std::move(d));
+
+ if (!decompressHelper.isValid()) {
+ // error occurred, error copied from QHttpNetworkConnectionPrivate::errorDetail
+ error(QNetworkReplyImpl::NetworkError::ProtocolFailure,
+ QCoreApplication::translate("QHttp", "Data corrupted"));
+ return;
+ }
+
+ if (!isHttpRedirectResponse()) {
+ if (decompressHelper.isCountingBytes())
+ bytesDownloaded += (decompressHelper.uncompressedSize() - uncompressedBefore);
+ setupTransferTimeout();
+ }
+
+ if (synchronous) {
+ d = QByteArray();
+ const qsizetype increments = 16 * 1024;
+ qint64 bytesRead = 0;
+ while (decompressHelper.hasData()) {
+ quint64 nextSize = quint64(d.size()) + quint64(increments);
+ if (nextSize > quint64(std::numeric_limits<QByteArray::size_type>::max())) {
+ error(QNetworkReplyImpl::NetworkError::ProtocolFailure,
+ QCoreApplication::translate("QHttp", "Data corrupted"));
+ return;
+ }
+ d.resize(nextSize);
+ bytesRead += decompressHelper.read(d.data() + bytesRead, increments);
+ }
+ d.resize(bytesRead);
+ // we're synchronous so we're not calling this function again; reset the decompressHelper
+ decompressHelper.clear();
+ }
+ }
+
// This is going to look a little strange. When downloading data while a
// HTTP redirect is happening (and enabled), we write the redirect
// response to the cache. However, we do not append it to our internal
// buffer as that will contain the response data only for the final
// response
- if (cacheSaveDevice)
+ // Note: For compressed data this is done in readData()
+ if (cacheSaveDevice && !decompressHelper.isValid()) {
cacheSaveDevice->write(d);
+ }
- if (!isHttpRedirectResponse()) {
+ // if decompressHelper is valid then we have compressed data, and this is handled above
+ if (!decompressHelper.isValid() && !isHttpRedirectResponse()) {
buffer.append(d);
- bytesDownloaded += d.size();
+ bytesDownloaded += dataSize;
setupTransferTimeout();
}
- bytesBuffered += d.size();
+ bytesBuffered += dataSize;
int pendingSignals = pendingDownloadDataEmissions->fetchAndSubAcquire(1) - 1;
if (pendingSignals > 0) {
@@ -1070,17 +1133,25 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
if (isHttpRedirectResponse())
return;
+ // This can occur when downloading compressed data as some of the data may be the content
+ // encoding's header. Don't emit anything for this.
+ if (previousBytesDownloaded == bytesDownloaded) {
+ if (readBufferMaxSize)
+ emit q->readBufferFreed(dataSize);
+ return;
+ }
+
QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
emit q->readyRead();
// emit readyRead before downloadProgress incase this will cause events to be
// processed and we get into a recursive call (as in QProgressDialog).
- if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
+ if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval
+ && (!decompressHelper.isValid() || decompressHelper.isCountingBytes())) {
downloadProgressSignalChoke.restart();
emit q->downloadProgress(bytesDownloaded,
totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
}
-
}
void QNetworkReplyHttpImplPrivate::replyFinished()
@@ -1207,6 +1278,7 @@ void QNetworkReplyHttpImplPrivate::followRedirect()
Q_Q(QNetworkReplyHttpImpl);
Q_ASSERT(managerPrivate);
+ decompressHelper.clear();
rawHeaders.clear();
cookedHeaders.clear();
@@ -1242,7 +1314,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
QSharedPointer<char> db,
qint64 contentLength,
qint64 removedContentLength,
- bool h2Used)
+ bool h2Used, bool isCompressed)
{
Q_Q(QNetworkReplyHttpImpl);
Q_UNUSED(contentLength);
@@ -1283,6 +1355,20 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
if (it->first.toLower() == "location")
value.clear();
+ if (isCompressed && !decompressHelper.isValid()
+ && it->first.compare("content-encoding", Qt::CaseInsensitive) == 0) {
+
+ if (!synchronous) // with synchronous all the data is expected to be handled at once
+ decompressHelper.setCountingBytesEnabled(true);
+
+ if (!decompressHelper.setEncoding(it->second)) {
+ // error occurred, error copied from QHttpNetworkConnectionPrivate::errorDetail
+ error(QNetworkReplyImpl::NetworkError::ProtocolFailure,
+ QCoreApplication::translate("QHttp", "Data corrupted"));
+ }
+ decompressHelper.setMinimumArchiveBombSize(request.minimumArchiveBombSize());
+ }
+
if (!value.isEmpty()) {
// Why are we appending values for headers which are already
// present?
@@ -2001,9 +2087,12 @@ void QNetworkReplyHttpImplPrivate::finished()
QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
- // if we don't know the total size of or we received everything save the cache
- if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
+ // if we don't know the total size of or we received everything save the cache.
+ // If the data is compressed then this is done in readData()
+ if ((totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
+ && !decompressHelper.isValid()) {
completeCacheSave();
+ }
// We check for errorCode too as in case of SSL handshake failure, we still
// get the HTTP redirect status code (301, 303 etc)
@@ -2041,6 +2130,9 @@ void QNetworkReplyHttpImplPrivate::error(QNetworkReplyImpl::NetworkError code, c
return;
}
+ if (decompressHelper.isValid())
+ decompressHelper.clear(); // Just get rid of any data that might be stored
+
errorCode = code;
q->setErrorString(errorMessage);
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
index 2bd6072c21..12fc484fe5 100644
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
@@ -73,6 +73,8 @@
Q_MOC_INCLUDE(<QtNetwork/QAuthenticator>)
+#include <private/qdecompresshelper_p.h>
+
QT_REQUIRE_CONFIG(http);
QT_BEGIN_NAMESPACE
@@ -108,9 +110,6 @@ public:
// From reply
Q_PRIVATE_SLOT(d_func(), void replyDownloadData(QByteArray))
Q_PRIVATE_SLOT(d_func(), void replyFinished())
- Q_PRIVATE_SLOT(d_func(), void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,
- int, QString, bool, QSharedPointer<char>,
- qint64, qint64, bool))
Q_PRIVATE_SLOT(d_func(), void replyDownloadProgressSlot(qint64,qint64))
Q_PRIVATE_SLOT(d_func(), void httpAuthenticationRequired(const QHttpNetworkRequest &, QAuthenticator *))
Q_PRIVATE_SLOT(d_func(), void httpError(QNetworkReply::NetworkError, const QString &))
@@ -258,6 +257,8 @@ public:
QNetworkRequest redirectRequest;
+ QDecompressHelper decompressHelper;
+
bool loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest);
void invalidateCache();
bool sendCacheContents(const QNetworkCacheMetaData &metaData);
@@ -274,7 +275,7 @@ public:
void replyDownloadData(QByteArray);
void replyFinished();
void replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &,
- bool, QSharedPointer<char>, qint64, qint64, bool);
+ bool, QSharedPointer<char>, qint64, qint64, bool, bool);
void replyDownloadProgressSlot(qint64,qint64);
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
void httpError(QNetworkReply::NetworkError error, const QString &errorString);