diff --git a/src/webui/httpconnection.cpp b/src/webui/httpconnection.cpp index 70defb11d..2547f5296 100644 --- a/src/webui/httpconnection.cpp +++ b/src/webui/httpconnection.cpp @@ -95,6 +95,7 @@ void HttpConnection::read() qWarning() << Q_FUNC_INFO << "header parsing error"; m_receivedData.clear(); m_generator.setStatusLine(400, "Bad Request"); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); return; } @@ -107,6 +108,7 @@ void HttpConnection::read() if (expected_length > 10000000 /* ~10MB */) { qWarning() << "Bad request: message too long"; m_generator.setStatusLine(400, "Bad Request"); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); m_receivedData.clear(); write(); return; @@ -127,6 +129,7 @@ void HttpConnection::read() if (m_parser.isError()) { qWarning() << Q_FUNC_INFO << "message parsing error"; m_generator.setStatusLine(400, "Bad Request"); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } else { respond(); @@ -188,6 +191,7 @@ void HttpConnection::respond() { if (nb_fail >= MAX_AUTH_FAILED_ATTEMPTS) { m_generator.setStatusLine(403, "Forbidden"); m_generator.setMessage(tr("Your IP address has been banned after too many failed authentication attempts.")); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); return; } @@ -197,6 +201,7 @@ void HttpConnection::respond() { qDebug("Auth is Empty..."); m_generator.setStatusLine(401, "Unauthorized"); m_generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+m_httpserver->generateNonce()+"\", opaque=\""+m_httpserver->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\""); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); return; } @@ -209,6 +214,7 @@ void HttpConnection::respond() { // Return unauthorized header m_generator.setStatusLine(401, "Unauthorized"); m_generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+m_httpserver->generateNonce()+"\", opaque=\""+m_httpserver->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\""); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); return; } @@ -226,6 +232,7 @@ void HttpConnection::respond() { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("png"); m_generator.setMessage(data); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } else { respondNotFound(); @@ -284,6 +291,7 @@ void HttpConnection::respond() { // need to reply to the Web UI before // actually shutting down. m_generator.setStatusLine(200, "OK"); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); qApp->processEvents(); // Exit application @@ -291,6 +299,7 @@ void HttpConnection::respond() { } else { respondCommand(command); m_generator.setStatusLine(200, "OK"); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } return; @@ -343,11 +352,13 @@ void HttpConnection::respond() { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt(ext); m_generator.setMessage(data); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } void HttpConnection::respondNotFound() { m_generator.setStatusLine(404, "File not found"); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } @@ -355,6 +366,7 @@ void HttpConnection::respondTorrentsJson() { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("js"); m_generator.setMessage(btjson::getTorrents()); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } @@ -362,6 +374,7 @@ void HttpConnection::respondGenPropertiesJson(const QString& hash) { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("js"); m_generator.setMessage(btjson::getPropertiesForTorrent(hash)); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } @@ -369,6 +382,7 @@ void HttpConnection::respondTrackersPropertiesJson(const QString& hash) { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("js"); m_generator.setMessage(btjson::getTrackersForTorrent(hash)); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } @@ -376,6 +390,7 @@ void HttpConnection::respondFilesPropertiesJson(const QString& hash) { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("js"); m_generator.setMessage(btjson::getFilesForTorrent(hash)); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } @@ -383,6 +398,7 @@ void HttpConnection::respondPreferencesJson() { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("js"); m_generator.setMessage(prefjson::getPreferences()); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } @@ -390,6 +406,7 @@ void HttpConnection::respondGlobalTransferInfoJson() { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("js"); m_generator.setMessage(btjson::getTransferInfo()); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } @@ -458,6 +475,7 @@ void HttpConnection::respondCommand(const QString& command) { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("html"); m_generator.setMessage(QString("")); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); return; } @@ -495,6 +513,7 @@ void HttpConnection::respondCommand(const QString& command) { #else m_generator.setMessage(QByteArray::number(QBtSession::instance()->getSession()->upload_rate_limit())); #endif + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); return; } @@ -506,6 +525,7 @@ void HttpConnection::respondCommand(const QString& command) { #else m_generator.setMessage(QByteArray::number(QBtSession::instance()->getSession()->download_rate_limit())); #endif + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); return; } @@ -516,6 +536,7 @@ void HttpConnection::respondCommand(const QString& command) { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("html"); m_generator.setMessage(QByteArray::number(h.upload_limit())); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } return; @@ -527,6 +548,7 @@ void HttpConnection::respondCommand(const QString& command) { m_generator.setStatusLine(200, "OK"); m_generator.setContentTypeByExt("html"); m_generator.setMessage(QByteArray::number(h.download_limit())); + m_generator.setContentEncoding(m_parser.acceptsEncoding()); write(); } return; diff --git a/src/webui/httprequestparser.cpp b/src/webui/httprequestparser.cpp index 30ac51b01..9ab8be2a0 100644 --- a/src/webui/httprequestparser.cpp +++ b/src/webui/httprequestparser.cpp @@ -158,3 +158,12 @@ Submit Query } } } + +bool HttpRequestParser::acceptsEncoding() { + QString encoding = m_header.value("Accept-Encoding"); + + if (!encoding.isEmpty() && encoding.contains("gzip", Qt::CaseInsensitive)) + return true; + + return false; +} diff --git a/src/webui/httprequestparser.h b/src/webui/httprequestparser.h index 64837255b..64efded52 100644 --- a/src/webui/httprequestparser.h +++ b/src/webui/httprequestparser.h @@ -48,6 +48,7 @@ public: const QList& torrents() const; void writeHeader(const QByteArray& ba); void writeMessage(const QByteArray& ba); + bool acceptsEncoding(); inline const QHttpRequestHeader& header() const { return m_header; } private: diff --git a/src/webui/httpresponsegenerator.cpp b/src/webui/httpresponsegenerator.cpp index 3c89cf5a3..2968a3dac 100644 --- a/src/webui/httpresponsegenerator.cpp +++ b/src/webui/httpresponsegenerator.cpp @@ -30,6 +30,7 @@ #include "httpresponsegenerator.h" +#include void HttpResponseGenerator::setMessage(const QByteArray& message) { @@ -64,3 +65,66 @@ void HttpResponseGenerator::setContentTypeByExt(const QString& ext) { return; } } + +bool HttpResponseGenerator::gCompress(QByteArray &dest_buffer) { + static const int BUFSIZE = 128 * 1024; + char tmp_buf[BUFSIZE]; + int ret; + + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.next_in = reinterpret_cast(m_message.data()); + strm.avail_in = m_message.length(); + strm.next_out = reinterpret_cast(tmp_buf); + strm.avail_out = BUFSIZE; + + //windowBits = 15|32 to enable gzip + ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, 15|16, 8, Z_DEFAULT_STRATEGY); + + if (ret != Z_OK) + return false; + + while (strm.avail_in != 0) + { + ret = deflate(&strm, Z_NO_FLUSH); + if (ret != Z_OK) + return false; + if (strm.avail_out == 0) + { + dest_buffer.append(tmp_buf, BUFSIZE); + strm.next_out = reinterpret_cast(tmp_buf); + strm.avail_out = BUFSIZE; + } + } + + int deflate_res = Z_OK; + while (deflate_res == Z_OK) { + if (strm.avail_out == 0) { + dest_buffer.append(tmp_buf, BUFSIZE); + strm.next_out = reinterpret_cast(tmp_buf); + strm.avail_out = BUFSIZE; + } + deflate_res = deflate(&strm, Z_FINISH); + } + + if (deflate_res != Z_STREAM_END) + return false; + dest_buffer.append(tmp_buf, BUFSIZE); + deflateEnd(&strm); + + return true; +} + +QByteArray HttpResponseGenerator::toByteArray() { + if (m_gzip) { + QByteArray dest_buf; + if (gCompress(dest_buf)) { + setValue("Content-Encoding", "gzip"); + m_message.swap(dest_buf); + } + } + + return QHttpResponseHeader::toString().toUtf8() + m_message; +} diff --git a/src/webui/httpresponsegenerator.h b/src/webui/httpresponsegenerator.h index ba46f4a22..adf9a0e82 100644 --- a/src/webui/httpresponsegenerator.h +++ b/src/webui/httpresponsegenerator.h @@ -38,15 +38,17 @@ class HttpResponseGenerator : public QHttpResponseHeader { public: + HttpResponseGenerator(): m_gzip(false) {} void setMessage(const QByteArray& message); void setMessage(const QString& message); void setContentTypeByExt(const QString& ext); - inline QByteArray toByteArray() const { - return QHttpResponseHeader::toString().toUtf8() + m_message; - } + void setContentEncoding(bool gzip) { m_gzip = gzip; } + QByteArray toByteArray(); private: + bool gCompress(QByteArray &dest_buffer); QByteArray m_message; + bool m_gzip; };