summaryrefslogtreecommitdiffstats
path: root/src/network/access
diff options
context:
space:
mode:
authorMårten Nordheim <marten.nordheim@qt.io>2020-05-06 18:24:07 +0200
committerMårten Nordheim <marten.nordheim@qt.io>2020-08-06 20:56:42 +0200
commitad1a5bf63fa63532c3267d4f365286dc78288651 (patch)
treea7f026b0397f95e1e2a44ce8acd61b60e3c59e51 /src/network/access
parentaf1544bda242d02690bd092f1d1ed7ca57659529 (diff)
QDecompressHelper: Add brotli support
Task-number: QTBUG-83269 Change-Id: If23b098ee76a4892e4c2c6ce5c635688d8d9138d Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Diffstat (limited to 'src/network/access')
-rw-r--r--src/network/access/access.pri4
-rw-r--r--src/network/access/qdecompresshelper.cpp115
-rw-r--r--src/network/access/qdecompresshelper_p.h6
3 files changed, 125 insertions, 0 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index 4add94a111..bb8f155d23 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -116,4 +116,8 @@ qtConfig(http) {
access/qhttpthreaddelegate_p.h \
access/qnetworkreplyhttpimpl_p.h \
access/qhttp2configuration.h
+
+ qtConfig(brotli) {
+ QMAKE_USE_PRIVATE += brotli
+ }
}
diff --git a/src/network/access/qdecompresshelper.cpp b/src/network/access/qdecompresshelper.cpp
index 5959a736d5..e56a411fc1 100644
--- a/src/network/access/qdecompresshelper.cpp
+++ b/src/network/access/qdecompresshelper.cpp
@@ -44,6 +44,10 @@
#include <zlib.h>
+#if QT_CONFIG(brotli)
+# include <brotli/decode.h>
+#endif
+
#include <array>
QT_BEGIN_NAMESPACE
@@ -57,6 +61,9 @@ struct ContentEncodingMapping
constexpr ContentEncodingMapping contentEncodingMapping[] {
{ "deflate", QDecompressHelper::Deflate },
{ "gzip", QDecompressHelper::GZip },
+#if QT_CONFIG(brotli)
+ { "br", QDecompressHelper::Brotli },
+#endif
};
QDecompressHelper::ContentEncoding encodingFromByteArray(const QByteArray &ce) noexcept
@@ -72,6 +79,13 @@ z_stream *toZlibPointer(void *ptr)
{
return static_cast<z_stream_s *>(ptr);
}
+
+#if QT_CONFIG(brotli)
+BrotliDecoderState *toBrotliPointer(void *ptr)
+{
+ return static_cast<BrotliDecoderState *>(ptr);
+}
+#endif
}
bool QDecompressHelper::isSupportedEncoding(const QByteArray &encoding)
@@ -134,6 +148,13 @@ bool QDecompressHelper::setEncoding(ContentEncoding ce)
decoderPointer = inflateStream;
break;
}
+ case Brotli:
+#if QT_CONFIG(brotli)
+ decoderPointer = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
+#else
+ Q_UNREACHABLE();
+#endif
+ break;
}
if (!decoderPointer) {
qWarning("Failed to initialize the decoder.");
@@ -325,6 +346,9 @@ qsizetype QDecompressHelper::read(char *data, qsizetype maxSize)
case GZip:
bytesRead = readZLib(data, maxSize);
break;
+ case Brotli:
+ bytesRead = readBrotli(data, maxSize);
+ break;
}
if (bytesRead == -1)
clear();
@@ -369,6 +393,14 @@ void QDecompressHelper::clear()
delete inflateStream;
break;
}
+ case Brotli: {
+#if QT_CONFIG(brotli)
+ BrotliDecoderState *brotliDecoderState = toBrotliPointer(decoderPointer);
+ if (brotliDecoderState)
+ BrotliDecoderDestroyInstance(brotliDecoderState);
+#endif
+ break;
+ }
}
decoderPointer = nullptr;
contentEncoding = None;
@@ -489,4 +521,87 @@ qsizetype QDecompressHelper::readZLib(char *data, const qsizetype maxSize)
return bytesDecoded;
}
+qsizetype QDecompressHelper::readBrotli(char *data, const qsizetype maxSize)
+{
+#if !QT_CONFIG(brotli)
+ Q_UNUSED(data);
+ Q_UNUSED(maxSize);
+ Q_UNREACHABLE();
+#else
+ qint64 bytesDecoded = 0;
+
+ BrotliDecoderState *brotliDecoderState = toBrotliPointer(decoderPointer);
+
+ while (decoderHasData && bytesDecoded < maxSize) {
+ Q_ASSERT(brotliUnconsumedDataPtr || BrotliDecoderHasMoreOutput(brotliDecoderState));
+ if (brotliUnconsumedDataPtr) {
+ Q_ASSERT(brotliUnconsumedAmount);
+ size_t toRead = std::min(size_t(maxSize - bytesDecoded), brotliUnconsumedAmount);
+ memcpy(data + bytesDecoded, brotliUnconsumedDataPtr, toRead);
+ bytesDecoded += toRead;
+ brotliUnconsumedAmount -= toRead;
+ brotliUnconsumedDataPtr += toRead;
+ if (brotliUnconsumedAmount == 0) {
+ brotliUnconsumedDataPtr = nullptr;
+ decoderHasData = false;
+ }
+ }
+ if (BrotliDecoderHasMoreOutput(brotliDecoderState) == BROTLI_TRUE) {
+ brotliUnconsumedDataPtr =
+ BrotliDecoderTakeOutput(brotliDecoderState, &brotliUnconsumedAmount);
+ decoderHasData = true;
+ }
+ }
+ if (bytesDecoded == maxSize)
+ return bytesDecoded;
+ Q_ASSERT(bytesDecoded < maxSize);
+
+ QByteArray input;
+ if (!compressedDataBuffer.isEmpty())
+ input = compressedDataBuffer.read();
+ const uint8_t *encodedPtr = reinterpret_cast<const uint8_t *>(input.constData());
+ size_t encodedBytesRemaining = input.size();
+
+ uint8_t *decodedPtr = reinterpret_cast<uint8_t *>(data + bytesDecoded);
+ size_t unusedDecodedSize = size_t(maxSize - bytesDecoded);
+ while (unusedDecodedSize > 0) {
+ auto previousUnusedDecodedSize = unusedDecodedSize;
+ BrotliDecoderResult result = BrotliDecoderDecompressStream(
+ brotliDecoderState, &encodedBytesRemaining, &encodedPtr, &unusedDecodedSize,
+ &decodedPtr, nullptr);
+ bytesDecoded += previousUnusedDecodedSize - unusedDecodedSize;
+
+ switch (result) {
+ case BROTLI_DECODER_RESULT_ERROR:
+ qWarning("Brotli error: %s",
+ BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotliDecoderState)));
+ return -1;
+ case BROTLI_DECODER_RESULT_SUCCESS:
+ BrotliDecoderDestroyInstance(brotliDecoderState);
+ decoderPointer = nullptr;
+ return bytesDecoded;
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
+ if (!compressedDataBuffer.isEmpty()) {
+ input = compressedDataBuffer.read();
+ encodedPtr = reinterpret_cast<const uint8_t *>(input.constData());
+ encodedBytesRemaining = input.size();
+ break;
+ }
+ return bytesDecoded;
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
+ // Some data is leftover inside the brotli decoder, remember for next time
+ decoderHasData = BrotliDecoderHasMoreOutput(brotliDecoderState);
+ Q_ASSERT(unusedDecodedSize == 0);
+ break;
+ }
+ }
+ if (encodedBytesRemaining) {
+ // Some input was left unused; move back to the buffer
+ input = input.right(QByteArray::size_type(encodedBytesRemaining));
+ compressedDataBuffer.prepend(input);
+ }
+ return bytesDecoded;
+#endif
+}
+
QT_END_NAMESPACE
diff --git a/src/network/access/qdecompresshelper_p.h b/src/network/access/qdecompresshelper_p.h
index 9d8ca245e1..851a1946d9 100644
--- a/src/network/access/qdecompresshelper_p.h
+++ b/src/network/access/qdecompresshelper_p.h
@@ -66,6 +66,7 @@ public:
None,
Deflate,
GZip,
+ Brotli,
};
QDecompressHelper() = default;
@@ -101,6 +102,7 @@ private:
qint64 encodedBytesAvailable() const;
qsizetype readZLib(char *data, qsizetype maxSize);
+ qsizetype readBrotli(char *data, qsizetype maxSize);
QByteDataBuffer compressedDataBuffer;
bool decoderHasData = false;
@@ -112,6 +114,10 @@ private:
ContentEncoding contentEncoding = None;
void *decoderPointer = nullptr;
+#if QT_CONFIG(brotli)
+ const uint8_t *brotliUnconsumedDataPtr = nullptr;
+ size_t brotliUnconsumedAmount = 0;
+#endif
};
QT_END_NAMESPACE