Skip to content
This repository has been archived by the owner on Oct 17, 2019. It is now read-only.

Commit

Permalink
Use the Accept-Content header to enable response compression
Browse files Browse the repository at this point in the history
fixes #86
fixes #89
  • Loading branch information
mujx committed Jan 21, 2018
1 parent 92a578f commit e37c2e3
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 3 deletions.
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ find_package(Qt5LinguistTools REQUIRED)
find_package(Qt5Concurrent REQUIRED)
find_package(Qt5Multimedia REQUIRED)

find_package(ZLIB)

if (ZLIB_FOUND)
include_directories(${ZLIB_INCLUDE_DIRS})
endif()

if (APPLE)
find_package(Qt5MacExtras REQUIRED)
endif(APPLE)
Expand Down Expand Up @@ -321,7 +327,7 @@ add_subdirectory(libs/matrix-structs)
include_directories(${matrix_structs_SOURCE_DIR}/include)
include_directories(${matrix_structs_SOURCE_DIR}/deps)

set(COMMON_LIBS matrix_structs Qt5::Widgets Qt5::Network Qt5::Concurrent)
set(COMMON_LIBS matrix_structs Qt5::Widgets Qt5::Network Qt5::Concurrent ${ZLIB_LIBRARIES})

if(APPVEYOR_BUILD)
set(NHEKO_LIBS ${COMMON_LIBS} lmdb)
Expand Down
1 change: 1 addition & 0 deletions include/MatrixClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ public slots:

private:
QNetworkReply *makeUploadRequest(QSharedPointer<QIODevice> iodev);
QByteArray uncompress(const QByteArray &data);

This comment has been minimized.

Copy link
@Ralith

Ralith Jan 21, 2018

Contributor

You shouldn't be calling zlib by hand. Qt already has this functionality built in.


// Client API prefix.
QString clientApiUrl_;
Expand Down
74 changes: 72 additions & 2 deletions src/MatrixClient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
#include <QSettings>
#include <QUrlQuery>

#include <zlib.h>

#include "Login.h"
#include "MatrixClient.h"
#include "Register.h"
Expand Down Expand Up @@ -252,6 +254,7 @@ MatrixClient::sync() noexcept
endpoint.setQuery(query);

QNetworkRequest request(QString(endpoint.toEncoded()));
request.setRawHeader(QByteArray("Accept-Encoding"), QByteArray("gzip, deflate"));

auto reply = get(request);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
Expand All @@ -264,8 +267,13 @@ MatrixClient::sync() noexcept
return;
}

auto data = reply->readAll();

if (reply->hasRawHeader(QByteArray("Content-Encoding")))

This comment has been minimized.

Copy link
@Ralith

Ralith Jan 21, 2018

Contributor

This is incorrect. The server is not guaranteed to respond with compressed data. Using Qt's existing, better-tested compression support would have avoided introducing this error.

data = uncompress(data);

try {
mtx::responses::Sync response = nlohmann::json::parse(reply->readAll());
mtx::responses::Sync response = nlohmann::json::parse(data);
emit syncCompleted(response);
} catch (std::exception &e) {
qWarning() << "Sync malformed response: " << e.what();
Expand Down Expand Up @@ -374,6 +382,7 @@ MatrixClient::initialSync() noexcept
endpoint.setQuery(query);

QNetworkRequest request(QString(endpoint.toEncoded()));
request.setRawHeader(QByteArray("Accept-Encoding"), QByteArray("gzip, deflate"));

This comment has been minimized.

Copy link
@Ralith

Ralith Jan 21, 2018

Contributor

If you look at the built-in logic, this actually disables the automatic compression support.

This comment has been minimized.

Copy link
@mujx

mujx Jan 21, 2018

Author Owner

This isn't always the case, it happens when qt is built with compression support. From my testing, using the manual header the response goes from 58 MB to 12 MB.

This comment has been minimized.

Copy link
@Ralith

Ralith Jan 21, 2018

Contributor

Why would you use a Qt not built with compression support, only to reimplement the disabled support in with complicated code of your own?


auto reply = get(request);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
Expand All @@ -386,8 +395,13 @@ MatrixClient::initialSync() noexcept
return;
}

auto data = reply->readAll();

if (reply->hasRawHeader(QByteArray("Content-Encoding")))
data = uncompress(data);

try {
mtx::responses::Sync response = nlohmann::json::parse(reply->readAll());
mtx::responses::Sync response = nlohmann::json::parse(data);
emit initialSyncCompleted(response);
} catch (std::exception &e) {
qWarning() << "Sync malformed response" << e.what();
Expand Down Expand Up @@ -1268,3 +1282,59 @@ MatrixClient::makeUploadRequest(QSharedPointer<QIODevice> iodev)

return reply;
}

QByteArray
MatrixClient::uncompress(const QByteArray &data)
{
/*
* https://stackoverflow.com/questions/2690328/qt-quncompress-gzip-data/7351507#7351507
*/

if (data.size() <= 4) {
qWarning("uncompress: Input data is truncated");
return QByteArray();
}

QByteArray result;

int ret;
z_stream strm;
static const int CHUNK_SIZE = 1024;
char out[CHUNK_SIZE];

/* allocate inflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = data.size();
strm.next_in = (Bytef *)(data.data());

ret = inflateInit2(&strm, 15 + 32); // gzip decoding
if (ret != Z_OK)
return QByteArray();

// run inflate()
do {
strm.avail_out = CHUNK_SIZE;
strm.next_out = (Bytef *)(out);

ret = inflate(&strm, Z_NO_FLUSH);
Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered

switch (ret) {
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
// fall through
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
return QByteArray();
}

result.append(out, CHUNK_SIZE - strm.avail_out);
} while (strm.avail_out == 0);

// clean up and return
inflateEnd(&strm);
return result;
}

0 comments on commit e37c2e3

Please sign in to comment.