6 #include "staticcompressed_p.h"
8 #include <Cutelyst/Application>
9 #include <Cutelyst/Request>
10 #include <Cutelyst/Response>
11 #include <Cutelyst/Context>
12 #include <Cutelyst/Engine>
14 #include <QMimeDatabase>
17 #include <QStandardPaths>
18 #include <QCoreApplication>
19 #include <QCryptographicHash>
20 #include <QLoggingCategory>
21 #include <QDataStream>
24 #ifdef CUTELYST_STATICCOMPRESSED_WITH_ZOPFLI
28 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
29 #include <brotli/encode.h>
34 Q_LOGGING_CATEGORY(C_STATICCOMPRESSED,
"cutelyst.plugin.staticcompressed", QtWarningMsg)
37 Plugin(parent), d_ptr(new StaticCompressedPrivate)
40 d->includePaths.append(parent->config(QStringLiteral(
"root")).toString());
51 d->includePaths.clear();
52 for (
const QString &path : paths) {
53 d->includePaths.append(QDir(path));
67 const QVariantMap config = app->
engine()->
config(QStringLiteral(
"Cutelyst_StaticCompressed_Plugin"));
68 const QString _defaultCacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String(
"/compressed-static");
69 d->cacheDir.setPath(config.value(QStringLiteral(
"cache_directory"), _defaultCacheDir).toString());
71 if (Q_UNLIKELY(!d->cacheDir.exists())) {
72 if (!d->cacheDir.mkpath(d->cacheDir.absolutePath())) {
73 qCCritical(C_STATICCOMPRESSED,
"Failed to create cache directory for compressed static files at \"%s\".", qPrintable(d->cacheDir.absolutePath()));
78 qCInfo(C_STATICCOMPRESSED,
"Compressed cache directory: %s", qPrintable(d->cacheDir.absolutePath()));
80 const QString _mimeTypes = config.value(QStringLiteral(
"mime_types"), QStringLiteral(
"text/css,application/javascript")).toString();
81 qCInfo(C_STATICCOMPRESSED,
"MIME Types: %s", qPrintable(_mimeTypes));
82 d->mimeTypes = _mimeTypes.split(u
',', Qt::SkipEmptyParts);
84 const QString _suffixes = config.value(QStringLiteral(
"suffixes"), QStringLiteral(
"js.map,css.map,min.js.map,min.css.map")).toString();
85 qCInfo(C_STATICCOMPRESSED,
"Suffixes: %s", qPrintable(_suffixes));
86 d->suffixes = _suffixes.split(u
',', Qt::SkipEmptyParts);
88 d->checkPreCompressed = config.value(QStringLiteral(
"check_pre_compressed"),
true).toBool();
89 qCInfo(C_STATICCOMPRESSED,
"Check for pre-compressed files: %s", d->checkPreCompressed ?
"true" :
"false");
91 d->onTheFlyCompression = config.value(QStringLiteral(
"on_the_fly_compression"),
true).toBool();
92 qCInfo(C_STATICCOMPRESSED,
"Compress static files on the fly: %s", d->onTheFlyCompression ?
"true" :
"false");
94 QStringList supportedCompressions{QStringLiteral(
"deflate"), QStringLiteral(
"gzip")};
97 d->zlibCompressionLevel = config.value(QStringLiteral(
"zlib_compression_level"), 9).toInt(&ok);
98 if (!ok || (d->zlibCompressionLevel < -1) || (d->zlibCompressionLevel > 9)) {
99 d->zlibCompressionLevel = -1;
102 #ifdef CUTELYST_STATICCOMPRESSED_WITH_ZOPFLI
103 d->zopfliIterations = config.value(QStringLiteral(
"zopfli_iterations"), 15).toInt(&ok);
104 if (!ok || (d->zopfliIterations < 0)) {
105 d->zopfliIterations = 15;
107 d->useZopfli = config.value(QStringLiteral(
"use_zopfli"),
false).toBool();
108 supportedCompressions << QStringLiteral(
"zopfli");
111 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
112 d->brotliQualityLevel = config.value(QStringLiteral(
"brotli_quality_level"), BROTLI_DEFAULT_QUALITY).toInt(&ok);
113 if (!ok || (d->brotliQualityLevel < BROTLI_MIN_QUALITY) || (d->brotliQualityLevel > BROTLI_MAX_QUALITY)) {
114 d->brotliQualityLevel = BROTLI_DEFAULT_QUALITY;
116 supportedCompressions << QStringLiteral(
"brotli");
119 qCInfo(C_STATICCOMPRESSED,
"Supported compressions: %s", qPrintable(supportedCompressions.join(u
',')));
122 d->beforePrepareAction(c, skipMethod);
128 void StaticCompressedPrivate::beforePrepareAction(
Context *c,
bool *skipMethod)
134 const QString path = c->req()->path();
135 const QRegularExpression _re = re;
137 for (
const QString &dir : dirs) {
138 if (path.startsWith(dir)) {
139 if (!locateCompressedFile(c, path)) {
143 res->
setBody(QStringLiteral(
"File not found: ") + path);
151 const QRegularExpressionMatch match = _re.match(path);
152 if (match.hasMatch() && locateCompressedFile(c, path)) {
157 bool StaticCompressedPrivate::locateCompressedFile(
Context *c,
const QString &relPath)
const
159 for (
const QDir &includePath : includePaths) {
160 const QString path = includePath.absoluteFilePath(relPath);
161 const QFileInfo fileInfo(path);
162 if (fileInfo.exists()) {
164 const QDateTime currentDateTime = fileInfo.lastModified();
170 static QMimeDatabase db;
172 const QMimeType mimeType = db.mimeTypeForFile(path, QMimeDatabase::MatchExtension);
173 QString contentEncoding;
174 QString compressedPath;
175 QString _mimeTypeName;
177 if (mimeType.isValid()) {
181 if (mimeType.isDefault()) {
182 if (path.endsWith(u
"css.map", Qt::CaseInsensitive) || path.endsWith(u
"js.map", Qt::CaseInsensitive)) {
183 _mimeTypeName = QStringLiteral(
"application/json");
187 if (mimeTypes.contains(mimeType.name(), Qt::CaseInsensitive) || suffixes.contains(fileInfo.completeSuffix(), Qt::CaseInsensitive)) {
189 const QString acceptEncoding = c->req()->
header(QStringLiteral(
"Accept-Encoding"));
190 qCDebug(C_STATICCOMPRESSED) <<
"Accept-Encoding:" << acceptEncoding;
192 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
193 if (acceptEncoding.contains(QLatin1String(
"br"), Qt::CaseInsensitive)) {
194 compressedPath = locateCacheFile(path, currentDateTime, Brotli) ;
195 if (!compressedPath.isEmpty()) {
196 qCDebug(C_STATICCOMPRESSED,
"Serving brotli compressed data from \"%s\".", qPrintable(compressedPath));
197 contentEncoding = QStringLiteral(
"br");
201 if (acceptEncoding.contains(QLatin1String(
"gzip"), Qt::CaseInsensitive)) {
202 compressedPath = locateCacheFile(path, currentDateTime, useZopfli ? Zopfli : Gzip);
203 if (!compressedPath.isEmpty()) {
204 qCDebug(C_STATICCOMPRESSED,
"Serving %s compressed data from \"%s\".", useZopfli ?
"zopfli" :
"gzip", qPrintable(compressedPath));
205 contentEncoding = QStringLiteral(
"gzip");
207 }
else if (acceptEncoding.contains(QLatin1String(
"deflate"), Qt::CaseInsensitive)) {
208 compressedPath = locateCacheFile(path, currentDateTime, Deflate);
209 if (!compressedPath.isEmpty()) {
210 qCDebug(C_STATICCOMPRESSED,
"Serving deflate compressed data from \"%s\".", qPrintable(compressedPath));
211 contentEncoding = QStringLiteral(
"deflate");
218 QFile *file = !compressedPath.isEmpty() ?
new QFile(compressedPath) : new QFile(path);
219 if (file->open(QFile::ReadOnly)) {
220 qCDebug(C_STATICCOMPRESSED) <<
"Serving" << path;
228 if (!_mimeTypeName.isEmpty()) {
230 }
else if (mimeType.isValid()) {
237 headers.
setHeader(QStringLiteral(
"CACHE_CONTROL"), QStringLiteral(
"public"));
239 if (!contentEncoding.isEmpty()) {
244 headers.
pushHeader(QStringLiteral(
"Vary"), QStringLiteral(
"Accept-Encoding"));
250 qCWarning(C_STATICCOMPRESSED) <<
"Could not serve" << path << file->errorString();
255 qCWarning(C_STATICCOMPRESSED) <<
"File not found" << relPath;
259 QString StaticCompressedPrivate::locateCacheFile(
const QString &origPath,
const QDateTime &origLastModified, Compression compression)
const
261 QString compressedPath;
265 switch (compression) {
268 suffix = QStringLiteral(
".gz");
270 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
272 suffix = QStringLiteral(
".br");
276 suffix = QStringLiteral(
".deflate");
279 Q_ASSERT_X(
false,
"locate cache file",
"invalid compression type");
283 if (checkPreCompressed) {
284 const QFileInfo origCompressed(origPath + suffix);
285 if (origCompressed.exists()) {
286 compressedPath = origCompressed.absoluteFilePath();
287 return compressedPath;
291 if (onTheFlyCompression) {
293 const QString path = cacheDir.absoluteFilePath(QString::fromLatin1(QCryptographicHash::hash(origPath.toUtf8(), QCryptographicHash::Md5).toHex()) + suffix);
294 const QFileInfo info(path);
296 if (info.exists() && (info.lastModified() > origLastModified)) {
297 compressedPath = path;
299 QLockFile lock(path + QLatin1String(
".lock"));
300 if (lock.tryLock(10)) {
301 switch (compression) {
302 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
304 if (compressBrotli(origPath, path)) {
305 compressedPath = path;
310 #ifdef CUTELYST_STATICCOMPRESSED_WITH_ZOPFLI
311 if (compressZopfli(origPath, path)) {
312 compressedPath = path;
317 if (compressGzip(origPath, path, origLastModified)) {
318 compressedPath = path;
322 if (compressDeflate(origPath, path)) {
323 compressedPath = path;
334 return compressedPath;
337 static const quint32 crc_32_tab[] = {
338 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
339 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
340 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
341 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
342 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
343 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
344 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
345 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
346 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
347 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
348 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
349 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
350 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
351 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
352 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
353 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
354 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
355 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
356 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
357 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
358 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
359 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
360 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
361 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
362 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
363 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
364 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
365 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
366 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
367 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
368 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
369 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
370 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
371 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
372 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
373 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
374 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
375 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
376 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
377 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
378 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
379 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
380 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
383 quint32 updateCRC32(
unsigned char ch, quint32 crc)
385 return (crc_32_tab[((crc) ^ (quint8(ch))) & 0xff] ^ ((crc) >> 8));
388 quint32 crc32buf(
const QByteArray& data)
390 return ~std::accumulate(
394 [](quint32 oldcrc32,
char buf){
return updateCRC32(buf, oldcrc32); });
397 bool StaticCompressedPrivate::compressGzip(
const QString &inputPath,
const QString &outputPath,
const QDateTime &origLastModified)
const
399 qCDebug(C_STATICCOMPRESSED,
"Compressing \"%s\" with gzip to \"%s\".", qPrintable(inputPath), qPrintable(outputPath));
401 QFile input(inputPath);
402 if (Q_UNLIKELY(!input.open(QIODevice::ReadOnly))) {
403 qCWarning(C_STATICCOMPRESSED) <<
"Can not open input file to compress with gzip:" << inputPath;
407 const QByteArray data = input.readAll();
408 if (Q_UNLIKELY(data.isEmpty())) {
409 qCWarning(C_STATICCOMPRESSED) <<
"Can not read input file or input file is empty:" << inputPath;
414 QByteArray compressedData = qCompress(data, zlibCompressionLevel);
417 QFile output(outputPath);
418 if (Q_UNLIKELY(!output.open(QIODevice::WriteOnly))) {
419 qCWarning(C_STATICCOMPRESSED) <<
"Can not open output file to compress with gzip:" << outputPath;
423 if (Q_UNLIKELY(compressedData.isEmpty())) {
424 qCWarning(C_STATICCOMPRESSED) <<
"Failed to compress file with gzip, compressed data is empty:" << inputPath;
425 if (output.exists()) {
426 if (Q_UNLIKELY(!output.remove())) {
427 qCWarning(C_STATICCOMPRESSED) <<
"Can not remove invalid compressed gzip file:" << outputPath;
435 compressedData.remove(0, 6);
436 compressedData.chop(4);
439 QDataStream headerStream(&header, QIODevice::WriteOnly);
441 headerStream << quint16(0x1f8b)
443 << quint32(origLastModified.toSecsSinceEpoch())
444 #if defined Q_OS_UNIX
446 #elif defined Q_OS_WIN
448 #elif defined Q_OS_MACOS
457 QDataStream footerStream(&footer, QIODevice::WriteOnly);
458 footerStream << crc32buf(data)
459 << quint32(data.size());
461 if (Q_UNLIKELY(output.write(header + compressedData + footer) < 0)) {
462 qCCritical(C_STATICCOMPRESSED,
"Failed to write compressed gzip file \"%s\": %s", qPrintable(inputPath), qPrintable(output.errorString()));
469 bool StaticCompressedPrivate::compressDeflate(
const QString &inputPath,
const QString &outputPath)
const
471 qCDebug(C_STATICCOMPRESSED,
"Compressing \"%s\" with deflate to \"%s\".", qPrintable(inputPath), qPrintable(outputPath));
473 QFile input(inputPath);
474 if (Q_UNLIKELY(!input.open(QIODevice::ReadOnly))) {
475 qCWarning(C_STATICCOMPRESSED) <<
"Can not open input file to compress with deflate:" << inputPath;
479 const QByteArray data = input.readAll();
480 if (Q_UNLIKELY(data.isEmpty())) {
481 qCWarning(C_STATICCOMPRESSED) <<
"Can not read input file or input file is empty:" << inputPath;
486 QByteArray compressedData = qCompress(data, zlibCompressionLevel);
489 QFile output(outputPath);
490 if (Q_UNLIKELY(!output.open(QIODevice::WriteOnly))) {
491 qCWarning(C_STATICCOMPRESSED) <<
"Can not open output file to compress with deflate:" << outputPath;
495 if (Q_UNLIKELY(compressedData.isEmpty())) {
496 qCWarning(C_STATICCOMPRESSED) <<
"Failed to compress file with deflate, compressed data is empty:" << inputPath;
497 if (output.exists()) {
498 if (Q_UNLIKELY(!output.remove())) {
499 qCWarning(C_STATICCOMPRESSED) <<
"Can not remove invalid compressed deflate file:" << outputPath;
507 compressedData.remove(0, 6);
508 compressedData.chop(4);
510 if (Q_UNLIKELY(output.write(compressedData) < 0)) {
511 qCCritical(C_STATICCOMPRESSED,
"Failed to write compressed deflate file \"%s\": %s", qPrintable(inputPath), qPrintable(output.errorString()));
518 #ifdef CUTELYST_STATICCOMPRESSED_WITH_ZOPFLI
519 bool StaticCompressedPrivate::compressZopfli(
const QString &inputPath,
const QString &outputPath)
const
521 qCDebug(C_STATICCOMPRESSED,
"Compressing \"%s\" with zopfli to \"%s\".", qPrintable(inputPath), qPrintable(outputPath));
523 QFile input(inputPath);
524 if (Q_UNLIKELY(!input.open(QIODevice::ReadOnly))) {
525 qCWarning(C_STATICCOMPRESSED) <<
"Can not open input file to compress with zopfli:" << inputPath;
529 const QByteArray data = input.readAll();
530 if (Q_UNLIKELY(data.isEmpty())) {
531 qCWarning(C_STATICCOMPRESSED) <<
"Can not read input file or input file is empty:" << inputPath;
536 ZopfliOptions options;
537 ZopfliInitOptions(&options);
538 options.numiterations = zopfliIterations;
540 unsigned char* out = 0;
543 ZopfliCompress(&options, ZopfliFormat::ZOPFLI_FORMAT_GZIP,
reinterpret_cast<const unsigned char *
>(data.constData()), data.size(), &out, &outSize);
547 QFile output(outputPath);
548 if (Q_UNLIKELY(!output.open(QIODevice::WriteOnly))) {
549 qCWarning(C_STATICCOMPRESSED) <<
"Can not open output file to compress with zopfli:" << outputPath;
551 if (Q_UNLIKELY(output.write(
reinterpret_cast<const char *
>(out), outSize) < 0)) {
552 qCCritical(C_STATICCOMPRESSED,
"Failed to write compressed zopfli file \"%s\": %s", qPrintable(inputPath), qPrintable(output.errorString()));
553 if (output.exists()) {
554 if (Q_UNLIKELY(!output.remove())) {
555 qCWarning(C_STATICCOMPRESSED) <<
"Can not remove invalid compressed zopfli file:" << outputPath;
563 qCWarning(C_STATICCOMPRESSED) <<
"Failed to compress file with zopfli, compressed data is empty:" << inputPath;
572 #ifdef CUTELYST_STATICCOMPRESSED_WITH_BROTLI
573 bool StaticCompressedPrivate::compressBrotli(
const QString &inputPath,
const QString &outputPath)
const
575 qCDebug(C_STATICCOMPRESSED,
"Compressing \"%s\" with brotli to \"%s\".", qPrintable(inputPath), qPrintable(outputPath));
577 QFile input(inputPath);
578 if (Q_UNLIKELY(!input.open(QIODevice::ReadOnly))) {
579 qCWarning(C_STATICCOMPRESSED) <<
"Can not open input file to compress with brotli:" << inputPath;
583 const QByteArray data = input.readAll();
584 if (Q_UNLIKELY(data.isEmpty())) {
585 qCWarning(C_STATICCOMPRESSED) <<
"Can not read input file or input file is empty:" << inputPath;
593 size_t outSize = BrotliEncoderMaxCompressedSize(
static_cast<size_t>(data.size()));
594 if (Q_LIKELY(outSize > 0)) {
595 const uint8_t *in = (
const uint8_t *) data.constData();
597 out = (uint8_t *) malloc(
sizeof(uint8_t) * (outSize+1));
598 if (Q_LIKELY(out !=
nullptr)) {
599 BROTLI_BOOL status = BrotliEncoderCompress(brotliQualityLevel, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, data.size(), in, &outSize, out);
600 if (Q_LIKELY(status == BROTLI_TRUE)) {
601 QFile output(outputPath);
602 if (Q_LIKELY(output.open(QIODevice::WriteOnly))) {
603 if (Q_LIKELY(output.write(
reinterpret_cast<const char *
>(out), outSize) > -1)) {
606 qCWarning(C_STATICCOMPRESSED,
"Failed to write brotli compressed data to output file \"%s\": %s", qPrintable(outputPath), qPrintable(output.errorString()));
607 if (output.exists()) {
608 if (Q_UNLIKELY(!output.remove())) {
609 qCWarning(C_STATICCOMPRESSED) <<
"Can not remove invalid compressed brotli file:" << outputPath;
614 qCWarning(C_STATICCOMPRESSED,
"Failed to open output file for brotli compression: %s", qPrintable(outputPath));
617 qCWarning(C_STATICCOMPRESSED,
"Failed to compress \"%s\" with brotli.", qPrintable(inputPath));
621 qCWarning(C_STATICCOMPRESSED,
"Can not allocate needed output buffer of size %lu for brotli compression.",
sizeof(uint8_t) * (outSize+1));
624 qCWarning(C_STATICCOMPRESSED,
"Needed output buffer too large to compress input of size %lu with brotli.",
static_cast<size_t>(data.size()));
631 #include "moc_staticcompressed.cpp"
The Cutelyst Application.
Engine * engine() const noexcept
void beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
Response * res() const noexcept
Response * response() const noexcept
QVariantMap config(const QString &entity) const
user configuration for the application
QString header(const QString &key) const
Headers headers() const noexcept
void setStatus(quint16 status) noexcept
Headers & headers() noexcept
void setBody(QIODevice *body)
void setContentType(const QString &type)
Deliver static files compressed on the fly or precompressed.
virtual ~StaticCompressed() override
void setIncludePaths(const QStringList &paths)
void setDirs(const QStringList &dirs)
virtual bool setup(Application *app) override
The Cutelyst namespace holds all public Cutelyst API.