Browse Source

Merge pull request #6654 from Chocobo1/persistence

Webui server fixes
adaptive-webui-19844
sledgehammer999 8 years ago committed by GitHub
parent
commit
0a5bb6685f
  1. 100
      .travis.yml
  2. 18
      configure
  3. 2
      configure.ac
  4. 2
      src/base/CMakeLists.txt
  5. 82
      src/base/http/connection.cpp
  6. 10
      src/base/http/connection.h
  7. 102
      src/base/http/responsegenerator.cpp
  8. 8
      src/base/http/responsegenerator.h
  9. 50
      src/base/http/server.cpp
  10. 4
      src/base/http/server.h
  11. 39
      src/base/http/types.h
  12. 4
      src/base/net/downloadhandler.cpp
  13. 6
      src/base/net/geoipmanager.cpp
  14. 139
      src/base/utils/gzip.cpp
  15. 4
      src/base/utils/gzip.h

100
.travis.yml

@ -33,16 +33,15 @@ notifications: @@ -33,16 +33,15 @@ notifications:
on_success: change
on_failure: change
# container-based builds
#sudo: false
cache:
ccache: true
directories:
- $HOME/hombebrew_cache
# opt-in Ubuntu Trusty
sudo: required
dist: trusty
# container-based builds
sudo: false
addons:
coverity_scan:
@ -62,7 +61,7 @@ addons: @@ -62,7 +61,7 @@ addons:
- sourceline: 'ppa:beineri/opt-qt551-trusty'
- sourceline: 'ppa:adrozdoff/cmake'
packages:
# packages list: https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
# packages list: https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
- autoconf
- automake
- cmake
@ -74,6 +73,7 @@ addons: @@ -74,6 +73,7 @@ addons:
# Qt 5.5.1
- qt55base
- qt55tools
before_install:
# only allow specific build for coverity scan, others will stop
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ] && ! [ "$TRAVIS_OS_NAME" = "linux" -a "$lt_branch" = "RC_1_0" -a "$gui" = true -a "$build_system" = "qmake" ]; then exit ; fi
@ -102,54 +102,51 @@ before_install: @@ -102,54 +102,51 @@ before_install:
- echo $build_system
- echo $ltconf
- echo $qbtconf
install:
- |
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
#- |
#if [ "$TRAVIS_OS_NAME" = "linux" ]; then
# build libtorrent from source
#if [ "$lt_branch" != "dist" ]; then
#cd "$HOME" && pwd && git clone --depth 1 https://github.com/arvidn/libtorrent.git --branch $lt_branch ;
#cd libtorrent && ./autotool.sh && ./configure $ltconf && make install ;
#fi ;
# ccache
if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
dpkg-query -L ccache && export use_ccache=true ;
ccache -V && ccache --show-stats && ccache --zero-stats ;
fi ;
fi
#cd "$HOME" && pwd && git clone --depth 1 https://github.com/arvidn/libtorrent.git --branch $lt_branch
#cd libtorrent && ./autotool.sh && ./configure $ltconf && make install
#fi
#fi
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
wget https://builds.shiki.hu/homebrew/version ;
# dependencies
brew update > /dev/null
brew install colormake ccache zlib
PATH="/usr/local/opt/ccache/libexec:$PATH"
brew link --force zlib
brew outdated "pkg-config" || brew upgrade "pkg-config"
wget https://builds.shiki.hu/homebrew/version
if ! cmp --quiet "version" "$HOME/hombebrew_cache/version" ; then
echo "Cached files are different from server. Downloading new ones." ;
echo "Cached files are different from server. Downloading new ones."
# First delete old files
rm -r "$HOME/hombebrew_cache" ;
mkdir "$HOME/hombebrew_cache";
cp "version" $HOME/hombebrew_cache ;
cd "$HOME/hombebrew_cache" ;
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb ;
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.0.10.el_capitan.bottle.tar.gz ;
wget https://builds.shiki.hu/homebrew/qt5.rb ;
wget https://builds.shiki.hu/homebrew/qt5-5.7.1_1.el_capitan.bottle.tar.gz ;
rm -r "$HOME/hombebrew_cache"
mkdir "$HOME/hombebrew_cache"
cp "version" $HOME/hombebrew_cache
cd "$HOME/hombebrew_cache"
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.0.10.el_capitan.bottle.tar.gz
wget https://builds.shiki.hu/homebrew/qt5.rb
wget https://builds.shiki.hu/homebrew/qt5-5.7.1_1.el_capitan.bottle.tar.gz
fi
# dependencies
brew update > /dev/null ;
brew install colormake ccache ;
if [ "$build_system" = "cmake" ]; then
brew unlink cmake
brew install cmake;
fi
brew outdated "pkg-config" || brew upgrade "pkg-config" ;
# Copy custom libtorrent bottle to homebrew's cache so it can find and install it
# Also install our custom libtorrent formula by passing the local path to it
# These 2 files are restored from Travis' cache.
cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.0.10.el_capitan.bottle.tar.gz" "$(brew --cache)" ;
brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb" ;
cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.0.10.el_capitan.bottle.tar.gz" "$(brew --cache)"
brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb"
if [ "$build_system" = "cmake" ]; then
brew install qt5 ;
brew link --force qt5 ;
brew unlink cmake
brew install cmake
brew install qt5
brew link --force qt5
ln -s /usr/local/opt/qt/mkspecs /usr/local/mkspecs
ln -s /usr/local/opt/qt/plugins /usr/local/plugins
else
@ -157,18 +154,17 @@ install: @@ -157,18 +154,17 @@ install:
# Copy custom qt5 bottle to homebrew's cache so it can find and install it
# Also install our custom qt5 formula by passing the local path to it
# These 2 files are restored from Travis' cache.
cp "$HOME/hombebrew_cache/qt5-5.7.1_1.el_capitan.bottle.tar.gz" "$(brew --cache)" ;
brew install "$HOME/hombebrew_cache/qt5.rb" ;
brew link --force qt5 ;
cp "$HOME/hombebrew_cache/qt5-5.7.1_1.el_capitan.bottle.tar.gz" "$(brew --cache)"
brew install "$HOME/hombebrew_cache/qt5.rb"
brew link --force qt5
fi
MY_CMAKE_OPENSSL_HINT="-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl/"
# ccache
if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
export PATH="/usr/local/opt/ccache/libexec:$PATH" && export use_ccache=true ;
ccache -V && ccache --show-stats && ccache --zero-stats ;
fi ;
fi
- |
if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
export use_ccache=true
ccache -V && ccache --show-stats && ccache --zero-stats
fi
script:
@ -184,9 +180,9 @@ script: @@ -184,9 +180,9 @@ script:
if [ "$build_system" = "qmake" ]; then
./bootstrap.sh && ./configure $qbtconf
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
sed -i "" -e "s/^\(CC.*&&\).*$/\1 $CC/" src/Makefile ; # workaround for Qt & ccache: https://bugreports.qt.io/browse/QTBUG-31034
sed -i "" -e "s/^\(CXX.*&&\).*$/\1 $CXX/" src/Makefile ;
sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs -Wno-inconsistent-missing-override/' src/Makefile ;
sed -i "" -e "s/^\(CC.*&&\).*$/\1 $CC/" src/Makefile # workaround for Qt & ccache: https://bugreports.qt.io/browse/QTBUG-31034
sed -i "" -e "s/^\(CXX.*&&\).*$/\1 $CXX/" src/Makefile
sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs -Wno-inconsistent-missing-override/' src/Makefile
fi
fi
- make && make install
@ -196,8 +192,8 @@ after_success: @@ -196,8 +192,8 @@ after_success:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd "$qbt_path/bin" ; fi
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
macdeployqt "$TRAVIS_BUILD_DIR/src/$qbt_exe.app" ;
cd "$TRAVIS_BUILD_DIR/src/$qbt_exe.app/Contents/MacOS" ;
macdeployqt "$TRAVIS_BUILD_DIR/src/$qbt_exe.app"
cd "$TRAVIS_BUILD_DIR/src/$qbt_exe.app/Contents/MacOS"
fi
- ./$qbt_exe --version

18
configure vendored

@ -5188,12 +5188,12 @@ if test -n "$zlib_CFLAGS"; then @@ -5188,12 +5188,12 @@ if test -n "$zlib_CFLAGS"; then
pkg_cv_zlib_CFLAGS="$zlib_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5
($PKG_CONFIG --exists --print-errors "zlib") 2>&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.5.2\""; } >&5
($PKG_CONFIG --exists --print-errors "zlib >= 1.2.5.2") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_zlib_CFLAGS=`$PKG_CONFIG --cflags "zlib" 2>/dev/null`
pkg_cv_zlib_CFLAGS=`$PKG_CONFIG --cflags "zlib >= 1.2.5.2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@ -5205,12 +5205,12 @@ if test -n "$zlib_LIBS"; then @@ -5205,12 +5205,12 @@ if test -n "$zlib_LIBS"; then
pkg_cv_zlib_LIBS="$zlib_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib\""; } >&5
($PKG_CONFIG --exists --print-errors "zlib") 2>&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"zlib >= 1.2.5.2\""; } >&5
($PKG_CONFIG --exists --print-errors "zlib >= 1.2.5.2") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_zlib_LIBS=`$PKG_CONFIG --libs "zlib" 2>/dev/null`
pkg_cv_zlib_LIBS=`$PKG_CONFIG --libs "zlib >= 1.2.5.2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@ -5231,14 +5231,14 @@ else @@ -5231,14 +5231,14 @@ else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
zlib_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib" 2>&1`
zlib_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "zlib >= 1.2.5.2" 2>&1`
else
zlib_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib" 2>&1`
zlib_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "zlib >= 1.2.5.2" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$zlib_PKG_ERRORS" >&5
as_fn_error $? "Package requirements (zlib) were not met:
as_fn_error $? "Package requirements (zlib >= 1.2.5.2) were not met:
$zlib_PKG_ERRORS

2
configure.ac

@ -166,7 +166,7 @@ PKG_CHECK_MODULES(libtorrent, @@ -166,7 +166,7 @@ PKG_CHECK_MODULES(libtorrent,
LIBS="$libtorrent_LIBS $LIBS"])
PKG_CHECK_MODULES(zlib,
[zlib],
[zlib >= 1.2.5.2],
[CPPFLAGS="$zlib_CFLAGS $CPPFLAGS"
LIBS="$zlib_LIBS $LIBS"])

2
src/base/CMakeLists.txt

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
find_package(ZLIB REQUIRED)
find_package(ZLIB 1.2.5.2 REQUIRED)
set(QBT_BASE_HEADERS
bittorrent/addtorrentparams.h

82
src/base/http/connection.cpp

@ -29,14 +29,14 @@ @@ -29,14 +29,14 @@
* Contact : chris@qbittorrent.org
*/
#include <QTcpSocket>
#include <QDebug>
#include "connection.h"
#include <QRegExp>
#include "types.h"
#include <QTcpSocket>
#include "irequesthandler.h"
#include "requestparser.h"
#include "responsegenerator.h"
#include "irequesthandler.h"
#include "connection.h"
using namespace Http;
@ -46,27 +46,33 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj @@ -46,27 +46,33 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj
, m_requestHandler(requestHandler)
{
m_socket->setParent(this);
m_idleTimer.start();
connect(m_socket, SIGNAL(readyRead()), SLOT(read()));
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
}
Connection::~Connection()
{
m_socket->close();
}
void Connection::read()
{
m_receivedData.append(m_socket->readAll());
m_idleTimer.restart();
m_receivedData.append(m_socket->readAll());
Request request;
RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request);
RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request); // TODO: transform request headers to lowercase
switch (err) {
case RequestParser::IncompleteRequest:
// Partial request waiting for the rest
break;
case RequestParser::BadRequest:
sendResponse(Response(400, "Bad Request"));
m_receivedData.clear();
break;
case RequestParser::NoError:
Environment env;
env.clientAddress = m_socket->peerAddress();
@ -74,25 +80,65 @@ void Connection::read() @@ -74,25 +80,65 @@ void Connection::read()
if (acceptsGzipEncoding(request.headers["accept-encoding"]))
response.headers[HEADER_CONTENT_ENCODING] = "gzip";
sendResponse(response);
m_receivedData.clear();
break;
}
}
void Connection::sendResponse(const Response &response)
{
m_socket->write(ResponseGenerator::generate(response));
m_socket->disconnectFromHost();
m_socket->write(toByteArray(response));
m_socket->close(); // TODO: remove when HTTP pipelining is supported
}
bool Connection::hasExpired(const qint64 timeout) const
{
return m_idleTimer.hasExpired(timeout);
}
bool Connection::isClosed() const
{
return (m_socket->state() == QAbstractSocket::UnconnectedState);
}
bool Connection::acceptsGzipEncoding(const QString &encoding)
bool Connection::acceptsGzipEncoding(QString codings)
{
QRegExp rx("(gzip)(;q=([^,]+))?");
if (rx.indexIn(encoding) >= 0) {
if (rx.cap(2).size() > 0)
// check if quality factor > 0
return (rx.cap(3).toDouble() > 0);
// if quality factor is not specified, then it's 1
// [rfc7231] 5.3.4. Accept-Encoding
const auto isCodingAvailable = [](const QStringList &list, const QString &encoding) -> bool
{
foreach (const QString &str, list) {
if (!str.startsWith(encoding))
continue;
// without quality values
if (str == encoding)
return true;
// [rfc7231] 5.3.1. Quality Values
const QStringRef substr = str.midRef(encoding.size() + 3); // ex. skip over "gzip;q="
bool ok = false;
const double qvalue = substr.toDouble(&ok);
if (!ok || (qvalue <= 0.0))
return false;
return true;
}
return false;
};
const QStringList list = codings.remove(' ').remove('\t').split(',', QString::SkipEmptyParts);
if (list.isEmpty())
return false;
const bool canGzip = isCodingAvailable(list, QLatin1String("gzip"));
if (canGzip)
return true;
}
const bool canAny = isCodingAvailable(list, QLatin1String("*"));
if (canAny)
return true;
return false;
}

10
src/base/http/connection.h

@ -33,12 +33,12 @@ @@ -33,12 +33,12 @@
#ifndef HTTP_CONNECTION_H
#define HTTP_CONNECTION_H
#include <QElapsedTimer>
#include <QObject>
#include "types.h"
QT_BEGIN_NAMESPACE
class QTcpSocket;
QT_END_NAMESPACE
namespace Http
{
@ -53,16 +53,20 @@ namespace Http @@ -53,16 +53,20 @@ namespace Http
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
~Connection();
bool hasExpired(qint64 timeout) const;
bool isClosed() const;
private slots:
void read();
private:
static bool acceptsGzipEncoding(const QString &encoding);
static bool acceptsGzipEncoding(QString codings);
void sendResponse(const Response &response);
QTcpSocket *m_socket;
IRequestHandler *m_requestHandler;
QByteArray m_receivedData;
QElapsedTimer m_idleTimer;
};
}

102
src/base/http/responsegenerator.cpp

@ -29,39 +29,79 @@ @@ -29,39 +29,79 @@
* Contact : chris@qbittorrent.org
*/
#include "base/utils/gzip.h"
#include "responsegenerator.h"
using namespace Http;
#include <QDateTime>
#include "base/utils/gzip.h"
QByteArray ResponseGenerator::generate(Response response)
QByteArray Http::toByteArray(Response response)
{
if (response.headers[HEADER_CONTENT_ENCODING] == "gzip") {
// A gzip seems to have 23 bytes overhead.
// Also "Content-Encoding: gzip\r\n" is 26 bytes long
// So we only benefit from gzip if the message is bigger than 23+26 = 49
// If the message is smaller than 49 bytes we actually send MORE data if we gzip
QByteArray dest_buf;
if ((response.content.size() > 49) && (Utils::Gzip::compress(response.content, dest_buf)))
response.content = dest_buf;
else
response.headers.remove(HEADER_CONTENT_ENCODING);
}
if (response.content.length() > 0)
response.headers[HEADER_CONTENT_LENGTH] = QString::number(response.content.length());
QString ret(QLatin1String("HTTP/1.1 %1 %2\r\n%3\r\n"));
QString header;
foreach (const QString& key, response.headers.keys())
header += QString("%1: %2\r\n").arg(key).arg(response.headers[key]);
ret = ret.arg(response.status.code).arg(response.status.text).arg(header);
// qDebug() << Q_FUNC_INFO;
// qDebug() << "HTTP Response header:";
// qDebug() << ret;
return ret.toUtf8() + response.content;
compressContent(response);
response.headers[HEADER_CONTENT_LENGTH] = QString::number(response.content.length());
response.headers[HEADER_DATE] = httpDate();
QByteArray buf;
buf.reserve(10 * 1024);
// Status Line
buf += QString("HTTP/%1 %2 %3")
.arg("1.1", // TODO: depends on request
QString::number(response.status.code),
response.status.text)
.toLatin1()
.append(CRLF);
// Header Fields
for (auto i = response.headers.constBegin(); i != response.headers.constEnd(); ++i)
buf += QString("%1: %2").arg(i.key(), i.value()).toLatin1().append(CRLF);
// the first empty line
buf += CRLF;
// message body // TODO: support HEAD request
buf += response.content;
return buf;
}
QString Http::httpDate()
{
// [RFC 7231] 7.1.1.1. Date/Time Formats
// example: "Sun, 06 Nov 1994 08:49:37 GMT"
return QLocale::c().toString(QDateTime::currentDateTimeUtc(), QLatin1String("ddd, dd MMM yyyy HH:mm:ss"))
.append(QLatin1String(" GMT"));
}
void Http::compressContent(Response &response)
{
if (response.headers.value(HEADER_CONTENT_ENCODING) != QLatin1String("gzip"))
return;
response.headers.remove(HEADER_CONTENT_ENCODING);
// for very small files, compressing them only wastes cpu cycles
const int contentSize = response.content.size();
if (contentSize <= 1024) // 1 kb
return;
// filter out known hard-to-compress types
const QString contentType = response.headers[HEADER_CONTENT_TYPE];
if ((contentType == CONTENT_TYPE_GIF) || (contentType == CONTENT_TYPE_PNG))
return;
// try compressing
bool ok = false;
const QByteArray compressedData = Utils::Gzip::compress(response.content, 6, &ok);
if (!ok)
return;
// "Content-Encoding: gzip\r\n" is 24 bytes long
if ((compressedData.size() + 24) >= contentSize)
return;
response.content = compressedData;
response.headers[HEADER_CONTENT_ENCODING] = QLatin1String("gzip");
}

8
src/base/http/responsegenerator.h

@ -37,11 +37,9 @@ @@ -37,11 +37,9 @@
namespace Http
{
class ResponseGenerator
{
public:
static QByteArray generate(Response response);
};
QByteArray toByteArray(Response response);
QString httpDate();
void compressContent(Response &response);
}
#endif // HTTP_RESPONSEGENERATOR_H

50
src/base/http/server.cpp

@ -30,8 +30,10 @@ @@ -30,8 +30,10 @@
#include "server.h"
#include <QMutableListIterator>
#include <QNetworkProxy>
#include <QStringList>
#include <QTimer>
#ifndef QT_NO_OPENSSL
#include <QSslSocket>
@ -41,6 +43,10 @@ @@ -41,6 +43,10 @@
#include "connection.h"
static const int KEEP_ALIVE_DURATION = 7; // seconds
static const int CONNECTIONS_LIMIT = 500;
static const int CONNECTIONS_SCAN_INTERVAL = 2; // seconds
using namespace Http;
Server::Server(IRequestHandler *requestHandler, QObject *parent)
@ -54,6 +60,10 @@ Server::Server(IRequestHandler *requestHandler, QObject *parent) @@ -54,6 +60,10 @@ Server::Server(IRequestHandler *requestHandler, QObject *parent)
#ifndef QT_NO_OPENSSL
QSslSocket::setDefaultCiphers(safeCipherList());
#endif
QTimer *dropConnectionTimer = new QTimer(this);
connect(dropConnectionTimer, &QTimer::timeout, this, &Server::dropTimedOutConnection);
dropConnectionTimer->start(CONNECTIONS_SCAN_INTERVAL * 1000);
}
Server::~Server()
@ -62,6 +72,8 @@ Server::~Server() @@ -62,6 +72,8 @@ Server::~Server()
void Server::incomingConnection(qintptr socketDescriptor)
{
if (m_connections.size() >= CONNECTIONS_LIMIT) return;
QTcpSocket *serverSocket;
#ifndef QT_NO_OPENSSL
if (m_https)
@ -70,20 +82,34 @@ void Server::incomingConnection(qintptr socketDescriptor) @@ -70,20 +82,34 @@ void Server::incomingConnection(qintptr socketDescriptor)
#endif
serverSocket = new QTcpSocket(this);
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
if (!serverSocket->setSocketDescriptor(socketDescriptor)) {
delete serverSocket;
return;
}
#ifndef QT_NO_OPENSSL
if (m_https) {
static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols);
static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key);
static_cast<QSslSocket *>(serverSocket)->setLocalCertificateChain(m_certificates);
static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone);
static_cast<QSslSocket *>(serverSocket)->startServerEncryption();
}
#endif
new Connection(serverSocket, m_requestHandler, this);
if (m_https) {
static_cast<QSslSocket *>(serverSocket)->setProtocol(QSsl::SecureProtocols);
static_cast<QSslSocket *>(serverSocket)->setPrivateKey(m_key);
static_cast<QSslSocket *>(serverSocket)->setLocalCertificateChain(m_certificates);
static_cast<QSslSocket *>(serverSocket)->setPeerVerifyMode(QSslSocket::VerifyNone);
static_cast<QSslSocket *>(serverSocket)->startServerEncryption();
}
else {
serverSocket->deleteLater();
#endif
Connection *c = new Connection(serverSocket, m_requestHandler, this);
m_connections.append(c);
}
void Server::dropTimedOutConnection()
{
QMutableListIterator<Connection *> i(m_connections);
while (i.hasNext()) {
auto connection = i.next();
if (connection->isClosed() || connection->hasExpired(KEEP_ALIVE_DURATION)) {
delete connection;
i.remove();
}
}
}

4
src/base/http/server.h

@ -60,10 +60,14 @@ namespace Http @@ -60,10 +60,14 @@ namespace Http
void disableHttps();
#endif
private slots:
void dropTimedOutConnection();
private:
void incomingConnection(qintptr socketDescriptor);
IRequestHandler *m_requestHandler;
QList<Connection *> m_connections; // for tracking persistence connections
#ifndef QT_NO_OPENSSL
QList<QSslCipher> safeCipherList() const;

39
src/base/http/types.h

@ -29,32 +29,35 @@ @@ -29,32 +29,35 @@
#ifndef HTTP_TYPES_H
#define HTTP_TYPES_H
#include <QString>
#include <QMap>
#include <QHostAddress>
#include <QString>
#include <QVector>
#include "base/types.h"
namespace Http
{
const QString HEADER_SET_COOKIE = "Set-Cookie";
const QString HEADER_CONTENT_TYPE = "Content-Type";
const QString HEADER_CONTENT_ENCODING = "Content-Encoding";
const QString HEADER_CONTENT_LENGTH = "Content-Length";
const QString HEADER_CACHE_CONTROL = "Cache-Control";
const QString HEADER_X_FRAME_OPTIONS = "X-Frame-Options";
const QString HEADER_X_XSS_PROTECTION = "X-XSS-Protection";
const QString HEADER_X_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options";
const QString HEADER_CONTENT_SECURITY_POLICY = "Content-Security-Policy";
const char HEADER_CACHE_CONTROL[] = "Cache-Control";
const char HEADER_CONTENT_ENCODING[] = "Content-Encoding";
const char HEADER_CONTENT_LENGTH[] = "Content-Length";
const char HEADER_CONTENT_SECURITY_POLICY[] = "Content-Security-Policy";
const char HEADER_CONTENT_TYPE[] = "Content-Type";
const char HEADER_DATE[] = "Date";
const char HEADER_SET_COOKIE[] = "Set-Cookie";
const char HEADER_X_CONTENT_TYPE_OPTIONS[] = "X-Content-Type-Options";
const char HEADER_X_FRAME_OPTIONS[] = "X-Frame-Options";
const char HEADER_X_XSS_PROTECTION[] = "X-XSS-Protection";
const char CONTENT_TYPE_CSS[] = "text/css; charset=UTF-8";
const char CONTENT_TYPE_GIF[] = "image/gif";
const char CONTENT_TYPE_HTML[] = "text/html; charset=UTF-8";
const char CONTENT_TYPE_JS[] = "application/javascript; charset=UTF-8";
const char CONTENT_TYPE_JSON[] = "application/json";
const char CONTENT_TYPE_PNG[] = "image/png";
const char CONTENT_TYPE_TXT[] = "text/plain; charset=UTF-8";
const QString CONTENT_TYPE_CSS = "text/css; charset=UTF-8";
const QString CONTENT_TYPE_GIF = "image/gif";
const QString CONTENT_TYPE_HTML = "text/html; charset=UTF-8";
const QString CONTENT_TYPE_JS = "application/javascript; charset=UTF-8";
const QString CONTENT_TYPE_JSON = "application/json";
const QString CONTENT_TYPE_PNG = "image/png";
const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8";
// portability: "\r\n" doesn't guarantee mapping to the correct value
const char CRLF[] = {0x0D, 0x0A, '\0'};
struct Environment
{

4
src/base/net/downloadhandler.cpp

@ -92,8 +92,8 @@ void DownloadHandler::processFinishedDownload() @@ -92,8 +92,8 @@ void DownloadHandler::processFinishedDownload()
// Success
QByteArray replyData = m_reply->readAll();
if (m_reply->rawHeader("Content-Encoding") == "gzip") {
// uncompress gzip reply
Utils::Gzip::uncompress(replyData, replyData);
// decompress gzip reply
replyData = Utils::Gzip::decompress(replyData);
}
if (m_saveToFile) {

6
src/base/net/geoipmanager.cpp

@ -416,8 +416,10 @@ void GeoIPManager::downloadFinished(const QString &url, QByteArray data) @@ -416,8 +416,10 @@ void GeoIPManager::downloadFinished(const QString &url, QByteArray data)
{
Q_UNUSED(url);
if (!Utils::Gzip::uncompress(data, data)) {
Logger::instance()->addMessage(tr("Could not uncompress GeoIP database file."), Log::WARNING);
bool ok = false;
data = Utils::Gzip::decompress(data, &ok);
if (!ok) {
Logger::instance()->addMessage(tr("Could not decompress GeoIP database file."), Log::WARNING);
return;
}

139
src/base/utils/gzip.cpp

@ -27,116 +27,123 @@ @@ -27,116 +27,123 @@
* exception statement from your version.
*/
#include "gzip.h"
#include <QByteArray>
#include <zlib.h>
#include "gzip.h"
#ifndef ZLIB_CONST
#define ZLIB_CONST // make z_stream.next_in const
#endif
#include <zlib.h>
bool Utils::Gzip::compress(QByteArray src, QByteArray &dest)
QByteArray Utils::Gzip::compress(const QByteArray &data, const int level, bool *ok)
{
static const int BUFSIZE = 128 * 1024;
char tmpBuf[BUFSIZE];
int ret;
if (ok) *ok = false;
if (data.isEmpty())
return {};
dest.clear();
const int BUFSIZE = 128 * 1024;
char tmpBuf[BUFSIZE] = {0};
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.next_in = reinterpret_cast<uchar *>(src.data());
strm.avail_in = src.length();
strm.next_out = reinterpret_cast<uchar *>(tmpBuf);
strm.next_in = reinterpret_cast<const Bytef *>(data.constData());
strm.avail_in = uInt(data.size());
strm.next_out = reinterpret_cast<Bytef *>(tmpBuf);
strm.avail_out = BUFSIZE;
// windowBits = 15 + 16 to enable gzip
// From the zlib manual: windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits
// to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper.
ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
int result = deflateInit2(&strm, level, Z_DEFLATED, (15 + 16), 9, Z_DEFAULT_STRATEGY);
if (result != Z_OK)
return {};
if (ret != Z_OK)
return false;
QByteArray output;
output.reserve(deflateBound(&strm, data.size()));
while (strm.avail_in != 0) {
ret = deflate(&strm, Z_NO_FLUSH);
if (ret != Z_OK)
return false;
// feed to deflate
while (strm.avail_in > 0) {
result = deflate(&strm, Z_NO_FLUSH);
if (strm.avail_out == 0) {
dest.append(tmpBuf, BUFSIZE);
strm.next_out = reinterpret_cast<uchar *>(tmpBuf);
strm.avail_out = BUFSIZE;
if (result != Z_OK) {
deflateEnd(&strm);
return {};
}
output.append(tmpBuf, (BUFSIZE - strm.avail_out));
strm.next_out = reinterpret_cast<Bytef *>(tmpBuf);
strm.avail_out = BUFSIZE;
}
int deflateRes = Z_OK;
while (deflateRes == Z_OK) {
if (strm.avail_out == 0) {
dest.append(tmpBuf, BUFSIZE);
strm.next_out = reinterpret_cast<uchar *>(tmpBuf);
strm.avail_out = BUFSIZE;
}
// flush the rest from deflate
while (result != Z_STREAM_END) {
result = deflate(&strm, Z_FINISH);
deflateRes = deflate(&strm, Z_FINISH);
output.append(tmpBuf, (BUFSIZE - strm.avail_out));
strm.next_out = reinterpret_cast<Bytef *>(tmpBuf);
strm.avail_out = BUFSIZE;
}
if (deflateRes != Z_STREAM_END)
return false;
dest.append(tmpBuf, BUFSIZE - strm.avail_out);
deflateEnd(&strm);
return true;
if (ok) *ok = true;
return output;
}
bool Utils::Gzip::uncompress(QByteArray src, QByteArray &dest)
QByteArray Utils::Gzip::decompress(const QByteArray &data, bool *ok)
{
dest.clear();
if (ok) *ok = false;
if (src.size() <= 4) {
qWarning("uncompress: Input data is truncated");
return false;
}
if (data.isEmpty())
return {};
z_stream strm;
static const int CHUNK_SIZE = 1024;
char out[CHUNK_SIZE];
const int BUFSIZE = 1024 * 1024;
char tmpBuf[BUFSIZE] = {0};
/* allocate inflate state */
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = static_cast<uint>(src.size());
strm.next_in = reinterpret_cast<uchar *>(src.data());
strm.next_in = reinterpret_cast<const Bytef *>(data.constData());
strm.avail_in = uInt(data.size());
strm.next_out = reinterpret_cast<Bytef *>(tmpBuf);
strm.avail_out = BUFSIZE;
const int windowBits = 15;
const int ENABLE_ZLIB_GZIP = 32;
// windowBits must be greater than or equal to the windowBits value provided to deflateInit2() while compressing
// Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection
int result = inflateInit2(&strm, (15 + 32));
if (result != Z_OK)
return {};
int ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP); // gzip decoding
if (ret != Z_OK)
return false;
QByteArray output;
// from lzbench, level 9 average compression ratio is: 31.92%, which decompression ratio is: 1 / 0.3192 = 3.13
output.reserve(data.size() * 3);
// run inflate()
do {
strm.avail_out = CHUNK_SIZE;
strm.next_out = reinterpret_cast<uchar *>(out);
// run inflate
while (true) {
result = inflate(&strm, Z_NO_FLUSH);
ret = inflate(&strm, Z_NO_FLUSH);
Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered
if (result == Z_STREAM_END) {
output.append(tmpBuf, (BUFSIZE - strm.avail_out));
break;
}
switch (ret) {
case Z_NEED_DICT:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
if (result != Z_OK) {
inflateEnd(&strm);
return false;
return {};
}
dest.append(out, CHUNK_SIZE - strm.avail_out);
output.append(tmpBuf, (BUFSIZE - strm.avail_out));
strm.next_out = reinterpret_cast<Bytef *>(tmpBuf);
strm.avail_out = BUFSIZE;
}
while (!strm.avail_out);
// clean up and return
inflateEnd(&strm);
return true;
if (ok) *ok = true;
return output;
}

4
src/base/utils/gzip.h

@ -36,8 +36,8 @@ namespace Utils @@ -36,8 +36,8 @@ namespace Utils
{
namespace Gzip
{
bool compress(QByteArray src, QByteArray &dest);
bool uncompress(QByteArray src, QByteArray &dest);
QByteArray compress(const QByteArray &data, int level = 6, bool *ok = nullptr);
QByteArray decompress(const QByteArray &data, bool *ok = nullptr);
}
}

Loading…
Cancel
Save