Skip to content

Commit

Permalink
Improve free disk space checking for WebAPI
Browse files Browse the repository at this point in the history
Use single free disk space checker instance for all the web sessions.

PR #19855.
Closes #19732.
  • Loading branch information
glassez committed Nov 12, 2023
1 parent 59d968e commit b824889
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 64 deletions.
4 changes: 2 additions & 2 deletions src/webui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ add_library(qbt_webui STATIC
api/apierror.h
api/appcontroller.h
api/authcontroller.h
api/freediskspacechecker.h
api/isessionmanager.h
api/logcontroller.h
api/rsscontroller.h
Expand All @@ -13,6 +12,7 @@ add_library(qbt_webui STATIC
api/torrentscontroller.h
api/transfercontroller.h
api/serialize/serialize_torrent.h
freediskspacechecker.h
webapplication.h
webui.h

Expand All @@ -21,14 +21,14 @@ add_library(qbt_webui STATIC
api/apierror.cpp
api/appcontroller.cpp
api/authcontroller.cpp
api/freediskspacechecker.cpp
api/logcontroller.cpp
api/rsscontroller.cpp
api/searchcontroller.cpp
api/synccontroller.cpp
api/torrentscontroller.cpp
api/transfercontroller.cpp
api/serialize/serialize_torrent.cpp
freediskspacechecker.cpp
webapplication.cpp
webui.cpp
)
Expand Down
43 changes: 7 additions & 36 deletions src/webui/api/synccontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
#include <QJsonArray>
#include <QJsonObject>
#include <QMetaObject>
#include <QThreadPool>

#include "base/algorithm.h"
#include "base/bittorrent/cachestatus.h"
Expand All @@ -50,13 +49,10 @@
#include "base/preferences.h"
#include "base/utils/string.h"
#include "apierror.h"
#include "freediskspacechecker.h"
#include "serialize/serialize_torrent.h"

namespace
{
const int FREEDISKSPACE_CHECK_TIMEOUT = 30000;

// Sync main data keys
const QString KEY_SYNC_MAINDATA_QUEUEING = u"queueing"_s;
const QString KEY_SYNC_MAINDATA_REFRESH_INTERVAL = u"refresh_interval"_s;
Expand Down Expand Up @@ -391,8 +387,11 @@ namespace
SyncController::SyncController(IApplication *app, QObject *parent)
: APIController(app, parent)
{
invokeChecker();
m_freeDiskSpaceElapsedTimer.start();
}

void SyncController::updateFreeDiskSpace(const qint64 freeDiskSpace)
{
m_freeDiskSpace = freeDiskSpace;
}

// The function returns the changed data from the server to synchronize with the web client.
Expand Down Expand Up @@ -552,7 +551,7 @@ void SyncController::makeMaindataSnapshot()
}

m_maindataSnapshot.serverState = getTransferInfo();
m_maindataSnapshot.serverState[KEY_TRANSFER_FREESPACEONDISK] = getFreeDiskSpace();
m_maindataSnapshot.serverState[KEY_TRANSFER_FREESPACEONDISK] = m_freeDiskSpace;
m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_QUEUEING] = session->isQueueingSystemEnabled();
m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled();
m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval();
Expand Down Expand Up @@ -661,7 +660,7 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu
m_removedTrackers.clear();

QVariantMap serverState = getTransferInfo();
serverState[KEY_TRANSFER_FREESPACEONDISK] = getFreeDiskSpace();
serverState[KEY_TRANSFER_FREESPACEONDISK] = m_freeDiskSpace;
serverState[KEY_SYNC_MAINDATA_QUEUEING] = session->isQueueingSystemEnabled();
serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled();
serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval();
Expand Down Expand Up @@ -782,34 +781,6 @@ void SyncController::torrentPeersAction()
setResult(generateSyncData(acceptedResponseId, data, m_lastAcceptedPeersResponse, m_lastPeersResponse));
}

qint64 SyncController::getFreeDiskSpace()
{
if (m_freeDiskSpaceElapsedTimer.hasExpired(FREEDISKSPACE_CHECK_TIMEOUT))
invokeChecker();

return m_freeDiskSpace;
}

void SyncController::invokeChecker()
{
if (m_isFreeDiskSpaceCheckerRunning)
return;

auto *freeDiskSpaceChecker = new FreeDiskSpaceChecker;
connect(freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, this, [this](const qint64 freeSpaceSize)
{
m_freeDiskSpace = freeSpaceSize;
m_isFreeDiskSpaceCheckerRunning = false;
m_freeDiskSpaceElapsedTimer.restart();
});
connect(freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, freeDiskSpaceChecker, &QObject::deleteLater);
m_isFreeDiskSpaceCheckerRunning = true;
QThreadPool::globalInstance()->start([freeDiskSpaceChecker]
{
freeDiskSpaceChecker->check();
});
}

void SyncController::onCategoryAdded(const QString &categoryName)
{
m_removedCategories.remove(categoryName);
Expand Down
15 changes: 4 additions & 11 deletions src/webui/api/synccontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,17 @@

#pragma once

#include <QElapsedTimer>
#include <QVariantMap>
#include <QSet>
#include <QVariantMap>

#include "base/bittorrent/infohash.h"
#include "apicontroller.h"

class QThread;

namespace BitTorrent
{
class Torrent;
}

class FreeDiskSpaceChecker;

class SyncController : public APIController
{
Q_OBJECT
Expand All @@ -54,14 +49,14 @@ class SyncController : public APIController

explicit SyncController(IApplication *app, QObject *parent = nullptr);

public slots:
void updateFreeDiskSpace(qint64 freeDiskSpace);

private slots:
void maindataAction();
void torrentPeersAction();

private:
qint64 getFreeDiskSpace();
void invokeChecker();

void makeMaindataSnapshot();
QJsonObject generateMaindataSyncData(int id, bool fullUpdate);

Expand All @@ -85,8 +80,6 @@ private slots:
void onTorrentTrackersChanged(BitTorrent::Torrent *torrent);

qint64 m_freeDiskSpace = 0;
QElapsedTimer m_freeDiskSpaceElapsedTimer;
bool m_isFreeDiskSpaceCheckerRunning = false;

QVariantMap m_lastPeersResponse;
QVariantMap m_lastAcceptedPeersResponse;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
*
* This program is free software; you can redistribute it and/or
Expand Down Expand Up @@ -31,8 +32,13 @@
#include "base/bittorrent/session.h"
#include "base/utils/fs.h"

qint64 FreeDiskSpaceChecker::lastResult() const
{
return m_lastResult;
}

void FreeDiskSpaceChecker::check()
{
const qint64 freeDiskSpace = Utils::Fs::freeDiskSpaceOnPath(BitTorrent::Session::instance()->savePath());
emit checked(freeDiskSpace);
m_lastResult = Utils::Fs::freeDiskSpaceOnPath(BitTorrent::Session::instance()->savePath());
emit checked(m_lastResult);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
*
* This program is free software; you can redistribute it and/or
Expand Down Expand Up @@ -38,9 +39,14 @@ class FreeDiskSpaceChecker final : public QObject
public:
using QObject::QObject;

qint64 lastResult() const;

public slots:
void check();

signals:
void checked(qint64 freeSpaceSize);

private:
qint64 m_lastResult = 0;
};
32 changes: 29 additions & 3 deletions src/webui/webapplication.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014, 2022 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2014, 2022-2023 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
Expand Down Expand Up @@ -29,16 +29,20 @@
#include "webapplication.h"

#include <algorithm>
#include <chrono>

#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QJsonDocument>
#include <QMetaObject>
#include <QMimeDatabase>
#include <QMimeType>
#include <QNetworkCookie>
#include <QRegularExpression>
#include <QThread>
#include <QTimer>
#include <QUrl>

#include "base/algorithm.h"
Expand All @@ -60,6 +64,7 @@
#include "api/synccontroller.h"
#include "api/torrentscontroller.h"
#include "api/transfercontroller.h"
#include "freediskspacechecker.h"

const int MAX_ALLOWED_FILESIZE = 10 * 1024 * 1024;
const QString DEFAULT_SESSION_COOKIE_NAME = u"SID"_s;
Expand All @@ -68,6 +73,10 @@ const QString WWW_FOLDER = u":/www"_s;
const QString PUBLIC_FOLDER = u"/public"_s;
const QString PRIVATE_FOLDER = u"/private"_s;

using namespace std::chrono_literals;

const std::chrono::seconds FREEDISKSPACE_CHECK_TIMEOUT = 30s;

namespace
{
QStringMap parseCookie(const QStringView cookieStr)
Expand Down Expand Up @@ -147,6 +156,9 @@ WebApplication::WebApplication(IApplication *app, QObject *parent)
, ApplicationComponent(app)
, m_cacheID {QString::number(Utils::Random::rand(), 36)}
, m_authController {new AuthController(this, app, this)}
, m_workerThread {new QThread}
, m_freeDiskSpaceChecker {new FreeDiskSpaceChecker}
, m_freeDiskSpaceCheckingTimer {new QTimer(this)}
{
declarePublicAPI(u"auth/login"_s);

Expand All @@ -163,6 +175,16 @@ WebApplication::WebApplication(IApplication *app, QObject *parent)
}
m_sessionCookieName = DEFAULT_SESSION_COOKIE_NAME;
}

m_freeDiskSpaceChecker->moveToThread(m_workerThread.get());
connect(m_workerThread.get(), &QThread::finished, m_freeDiskSpaceChecker, &QObject::deleteLater);
m_workerThread->start();

m_freeDiskSpaceCheckingTimer->setInterval(FREEDISKSPACE_CHECK_TIMEOUT);
m_freeDiskSpaceCheckingTimer->setSingleShot(true);
connect(m_freeDiskSpaceCheckingTimer, &QTimer::timeout, m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::check);
connect(m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, m_freeDiskSpaceCheckingTimer, qOverload<>(&QTimer::start));
QMetaObject::invokeMethod(m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::check);
}

WebApplication::~WebApplication()
Expand Down Expand Up @@ -690,14 +712,18 @@ void WebApplication::sessionStart()
});

m_currentSession = new WebSession(generateSid(), app());
m_sessions[m_currentSession->id()] = m_currentSession;

m_currentSession->registerAPIController<AppController>(u"app"_s);
m_currentSession->registerAPIController<LogController>(u"log"_s);
m_currentSession->registerAPIController<RSSController>(u"rss"_s);
m_currentSession->registerAPIController<SearchController>(u"search"_s);
m_currentSession->registerAPIController<SyncController>(u"sync"_s);
m_currentSession->registerAPIController<TorrentsController>(u"torrents"_s);
m_currentSession->registerAPIController<TransferController>(u"transfer"_s);
m_sessions[m_currentSession->id()] = m_currentSession;

auto *syncController = m_currentSession->registerAPIController<SyncController>(u"sync"_s);
syncController->updateFreeDiskSpace(m_freeDiskSpaceChecker->lastResult());
connect(m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, syncController, &SyncController::updateFreeDiskSpace);

QNetworkCookie cookie {m_sessionCookieName.toLatin1(), m_currentSession->id().toUtf8()};
cookie.setHttpOnly(true);
Expand Down
26 changes: 18 additions & 8 deletions src/webui/webapplication.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014, 2017, 2022 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2014, 2017, 2022-2023 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
Expand Down Expand Up @@ -49,13 +49,17 @@
#include "base/http/types.h"
#include "base/path.h"
#include "base/utils/net.h"
#include "base/utils/thread.h"
#include "base/utils/version.h"
#include "api/isessionmanager.h"

inline const Utils::Version<3, 2> API_VERSION {2, 9, 3};

class QTimer;

class APIController;
class AuthController;
class FreeDiskSpaceChecker;
class WebApplication;

class WebSession final : public QObject, public ApplicationComponent, public ISession
Expand All @@ -69,10 +73,12 @@ class WebSession final : public QObject, public ApplicationComponent, public ISe
void updateTimestamp();

template <typename T>
void registerAPIController(const QString &scope)
T *registerAPIController(const QString &scope)
{
static_assert(std::is_base_of_v<APIController, T>, "Class should be derived from APIController.");
m_apiControllers[scope] = new T(app(), this);
auto *controller = new T(app(), this);
m_apiControllers[scope] = controller;
return controller;
}

APIController *getAPIController(const QString &scope) const;
Expand All @@ -97,18 +103,18 @@ class WebApplication final

Http::Response processRequest(const Http::Request &request, const Http::Environment &env) override;

QString clientId() const override;
WebSession *session() override;
void sessionStart() override;
void sessionEnd() override;

const Http::Request &request() const;
const Http::Environment &env() const;

void setUsername(const QString &username);
void setPasswordHash(const QByteArray &passwordHash);

private:
QString clientId() const override;
WebSession *session() override;
void sessionStart() override;
void sessionEnd() override;

void doProcessRequest();
void configure();

Expand Down Expand Up @@ -244,4 +250,8 @@ class WebApplication final
QHostAddress m_clientAddress;

QVector<Http::Header> m_prebuiltHeaders;

Utils::Thread::UniquePtr m_workerThread;
FreeDiskSpaceChecker *m_freeDiskSpaceChecker = nullptr;
QTimer *m_freeDiskSpaceCheckingTimer = nullptr;
};

0 comments on commit b824889

Please sign in to comment.