Improve free disk space checking for WebAPI

Use single free disk space checker instance for all the web sessions.

PR #19855.
Closes #19732.
This commit is contained in:
Vladimir Golovnev 2023-11-07 12:44:27 +03:00 committed by Vladimir Golovnev (Glassez)
parent 59d968e116
commit b824889d07
No known key found for this signature in database
GPG key ID: 52A2C7DEE2DFA6F7
8 changed files with 76 additions and 64 deletions

View file

@ -4,7 +4,6 @@ add_library(qbt_webui STATIC
api/apierror.h api/apierror.h
api/appcontroller.h api/appcontroller.h
api/authcontroller.h api/authcontroller.h
api/freediskspacechecker.h
api/isessionmanager.h api/isessionmanager.h
api/logcontroller.h api/logcontroller.h
api/rsscontroller.h api/rsscontroller.h
@ -13,6 +12,7 @@ add_library(qbt_webui STATIC
api/torrentscontroller.h api/torrentscontroller.h
api/transfercontroller.h api/transfercontroller.h
api/serialize/serialize_torrent.h api/serialize/serialize_torrent.h
freediskspacechecker.h
webapplication.h webapplication.h
webui.h webui.h
@ -21,7 +21,6 @@ add_library(qbt_webui STATIC
api/apierror.cpp api/apierror.cpp
api/appcontroller.cpp api/appcontroller.cpp
api/authcontroller.cpp api/authcontroller.cpp
api/freediskspacechecker.cpp
api/logcontroller.cpp api/logcontroller.cpp
api/rsscontroller.cpp api/rsscontroller.cpp
api/searchcontroller.cpp api/searchcontroller.cpp
@ -29,6 +28,7 @@ add_library(qbt_webui STATIC
api/torrentscontroller.cpp api/torrentscontroller.cpp
api/transfercontroller.cpp api/transfercontroller.cpp
api/serialize/serialize_torrent.cpp api/serialize/serialize_torrent.cpp
freediskspacechecker.cpp
webapplication.cpp webapplication.cpp
webui.cpp webui.cpp
) )

View file

@ -33,7 +33,6 @@
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>
#include <QMetaObject> #include <QMetaObject>
#include <QThreadPool>
#include "base/algorithm.h" #include "base/algorithm.h"
#include "base/bittorrent/cachestatus.h" #include "base/bittorrent/cachestatus.h"
@ -50,13 +49,10 @@
#include "base/preferences.h" #include "base/preferences.h"
#include "base/utils/string.h" #include "base/utils/string.h"
#include "apierror.h" #include "apierror.h"
#include "freediskspacechecker.h"
#include "serialize/serialize_torrent.h" #include "serialize/serialize_torrent.h"
namespace namespace
{ {
const int FREEDISKSPACE_CHECK_TIMEOUT = 30000;
// Sync main data keys // Sync main data keys
const QString KEY_SYNC_MAINDATA_QUEUEING = u"queueing"_s; const QString KEY_SYNC_MAINDATA_QUEUEING = u"queueing"_s;
const QString KEY_SYNC_MAINDATA_REFRESH_INTERVAL = u"refresh_interval"_s; const QString KEY_SYNC_MAINDATA_REFRESH_INTERVAL = u"refresh_interval"_s;
@ -391,8 +387,11 @@ namespace
SyncController::SyncController(IApplication *app, QObject *parent) SyncController::SyncController(IApplication *app, QObject *parent)
: APIController(app, 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. // The function returns the changed data from the server to synchronize with the web client.
@ -552,7 +551,7 @@ void SyncController::makeMaindataSnapshot()
} }
m_maindataSnapshot.serverState = getTransferInfo(); 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_QUEUEING] = session->isQueueingSystemEnabled();
m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled(); m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled();
m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval(); m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval();
@ -661,7 +660,7 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu
m_removedTrackers.clear(); m_removedTrackers.clear();
QVariantMap serverState = getTransferInfo(); 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_QUEUEING] = session->isQueueingSystemEnabled();
serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled(); serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled();
serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval(); serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval();
@ -782,34 +781,6 @@ void SyncController::torrentPeersAction()
setResult(generateSyncData(acceptedResponseId, data, m_lastAcceptedPeersResponse, m_lastPeersResponse)); 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) void SyncController::onCategoryAdded(const QString &categoryName)
{ {
m_removedCategories.remove(categoryName); m_removedCategories.remove(categoryName);

View file

@ -28,22 +28,17 @@
#pragma once #pragma once
#include <QElapsedTimer>
#include <QVariantMap>
#include <QSet> #include <QSet>
#include <QVariantMap>
#include "base/bittorrent/infohash.h" #include "base/bittorrent/infohash.h"
#include "apicontroller.h" #include "apicontroller.h"
class QThread;
namespace BitTorrent namespace BitTorrent
{ {
class Torrent; class Torrent;
} }
class FreeDiskSpaceChecker;
class SyncController : public APIController class SyncController : public APIController
{ {
Q_OBJECT Q_OBJECT
@ -54,14 +49,14 @@ public:
explicit SyncController(IApplication *app, QObject *parent = nullptr); explicit SyncController(IApplication *app, QObject *parent = nullptr);
public slots:
void updateFreeDiskSpace(qint64 freeDiskSpace);
private slots: private slots:
void maindataAction(); void maindataAction();
void torrentPeersAction(); void torrentPeersAction();
private: private:
qint64 getFreeDiskSpace();
void invokeChecker();
void makeMaindataSnapshot(); void makeMaindataSnapshot();
QJsonObject generateMaindataSyncData(int id, bool fullUpdate); QJsonObject generateMaindataSyncData(int id, bool fullUpdate);
@ -85,8 +80,6 @@ private:
void onTorrentTrackersChanged(BitTorrent::Torrent *torrent); void onTorrentTrackersChanged(BitTorrent::Torrent *torrent);
qint64 m_freeDiskSpace = 0; qint64 m_freeDiskSpace = 0;
QElapsedTimer m_freeDiskSpaceElapsedTimer;
bool m_isFreeDiskSpaceCheckerRunning = false;
QVariantMap m_lastPeersResponse; QVariantMap m_lastPeersResponse;
QVariantMap m_lastAcceptedPeersResponse; QVariantMap m_lastAcceptedPeersResponse;

View file

@ -1,5 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com> * Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -31,8 +32,13 @@
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
qint64 FreeDiskSpaceChecker::lastResult() const
{
return m_lastResult;
}
void FreeDiskSpaceChecker::check() void FreeDiskSpaceChecker::check()
{ {
const qint64 freeDiskSpace = Utils::Fs::freeDiskSpaceOnPath(BitTorrent::Session::instance()->savePath()); m_lastResult = Utils::Fs::freeDiskSpaceOnPath(BitTorrent::Session::instance()->savePath());
emit checked(freeDiskSpace); emit checked(m_lastResult);
} }

View file

@ -1,5 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2023 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com> * Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -38,9 +39,14 @@ class FreeDiskSpaceChecker final : public QObject
public: public:
using QObject::QObject; using QObject::QObject;
qint64 lastResult() const;
public slots: public slots:
void check(); void check();
signals: signals:
void checked(qint64 freeSpaceSize); void checked(qint64 freeSpaceSize);
private:
qint64 m_lastResult = 0;
}; };

View file

@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -29,16 +29,20 @@
#include "webapplication.h" #include "webapplication.h"
#include <algorithm> #include <algorithm>
#include <chrono>
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QJsonDocument> #include <QJsonDocument>
#include <QMetaObject>
#include <QMimeDatabase> #include <QMimeDatabase>
#include <QMimeType> #include <QMimeType>
#include <QNetworkCookie> #include <QNetworkCookie>
#include <QRegularExpression> #include <QRegularExpression>
#include <QThread>
#include <QTimer>
#include <QUrl> #include <QUrl>
#include "base/algorithm.h" #include "base/algorithm.h"
@ -60,6 +64,7 @@
#include "api/synccontroller.h" #include "api/synccontroller.h"
#include "api/torrentscontroller.h" #include "api/torrentscontroller.h"
#include "api/transfercontroller.h" #include "api/transfercontroller.h"
#include "freediskspacechecker.h"
const int MAX_ALLOWED_FILESIZE = 10 * 1024 * 1024; const int MAX_ALLOWED_FILESIZE = 10 * 1024 * 1024;
const QString DEFAULT_SESSION_COOKIE_NAME = u"SID"_s; const QString DEFAULT_SESSION_COOKIE_NAME = u"SID"_s;
@ -68,6 +73,10 @@ const QString WWW_FOLDER = u":/www"_s;
const QString PUBLIC_FOLDER = u"/public"_s; const QString PUBLIC_FOLDER = u"/public"_s;
const QString PRIVATE_FOLDER = u"/private"_s; const QString PRIVATE_FOLDER = u"/private"_s;
using namespace std::chrono_literals;
const std::chrono::seconds FREEDISKSPACE_CHECK_TIMEOUT = 30s;
namespace namespace
{ {
QStringMap parseCookie(const QStringView cookieStr) QStringMap parseCookie(const QStringView cookieStr)
@ -147,6 +156,9 @@ WebApplication::WebApplication(IApplication *app, QObject *parent)
, ApplicationComponent(app) , ApplicationComponent(app)
, m_cacheID {QString::number(Utils::Random::rand(), 36)} , m_cacheID {QString::number(Utils::Random::rand(), 36)}
, m_authController {new AuthController(this, app, this)} , 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); declarePublicAPI(u"auth/login"_s);
@ -163,6 +175,16 @@ WebApplication::WebApplication(IApplication *app, QObject *parent)
} }
m_sessionCookieName = DEFAULT_SESSION_COOKIE_NAME; 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() WebApplication::~WebApplication()
@ -690,14 +712,18 @@ void WebApplication::sessionStart()
}); });
m_currentSession = new WebSession(generateSid(), app()); m_currentSession = new WebSession(generateSid(), app());
m_sessions[m_currentSession->id()] = m_currentSession;
m_currentSession->registerAPIController<AppController>(u"app"_s); m_currentSession->registerAPIController<AppController>(u"app"_s);
m_currentSession->registerAPIController<LogController>(u"log"_s); m_currentSession->registerAPIController<LogController>(u"log"_s);
m_currentSession->registerAPIController<RSSController>(u"rss"_s); m_currentSession->registerAPIController<RSSController>(u"rss"_s);
m_currentSession->registerAPIController<SearchController>(u"search"_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<TorrentsController>(u"torrents"_s);
m_currentSession->registerAPIController<TransferController>(u"transfer"_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()}; QNetworkCookie cookie {m_sessionCookieName.toLatin1(), m_currentSession->id().toUtf8()};
cookie.setHttpOnly(true); cookie.setHttpOnly(true);

View file

@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -49,13 +49,17 @@
#include "base/http/types.h" #include "base/http/types.h"
#include "base/path.h" #include "base/path.h"
#include "base/utils/net.h" #include "base/utils/net.h"
#include "base/utils/thread.h"
#include "base/utils/version.h" #include "base/utils/version.h"
#include "api/isessionmanager.h" #include "api/isessionmanager.h"
inline const Utils::Version<3, 2> API_VERSION {2, 9, 3}; inline const Utils::Version<3, 2> API_VERSION {2, 9, 3};
class QTimer;
class APIController; class APIController;
class AuthController; class AuthController;
class FreeDiskSpaceChecker;
class WebApplication; class WebApplication;
class WebSession final : public QObject, public ApplicationComponent, public ISession class WebSession final : public QObject, public ApplicationComponent, public ISession
@ -69,10 +73,12 @@ public:
void updateTimestamp(); void updateTimestamp();
template <typename T> 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."); 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; APIController *getAPIController(const QString &scope) const;
@ -97,11 +103,6 @@ public:
Http::Response processRequest(const Http::Request &request, const Http::Environment &env) override; 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::Request &request() const;
const Http::Environment &env() const; const Http::Environment &env() const;
@ -109,6 +110,11 @@ public:
void setPasswordHash(const QByteArray &passwordHash); void setPasswordHash(const QByteArray &passwordHash);
private: private:
QString clientId() const override;
WebSession *session() override;
void sessionStart() override;
void sessionEnd() override;
void doProcessRequest(); void doProcessRequest();
void configure(); void configure();
@ -244,4 +250,8 @@ private:
QHostAddress m_clientAddress; QHostAddress m_clientAddress;
QVector<Http::Header> m_prebuiltHeaders; QVector<Http::Header> m_prebuiltHeaders;
Utils::Thread::UniquePtr m_workerThread;
FreeDiskSpaceChecker *m_freeDiskSpaceChecker = nullptr;
QTimer *m_freeDiskSpaceCheckingTimer = nullptr;
}; };

View file

@ -3,7 +3,6 @@ HEADERS += \
$$PWD/api/apierror.h \ $$PWD/api/apierror.h \
$$PWD/api/appcontroller.h \ $$PWD/api/appcontroller.h \
$$PWD/api/authcontroller.h \ $$PWD/api/authcontroller.h \
$$PWD/api/freediskspacechecker.h \
$$PWD/api/isessionmanager.h \ $$PWD/api/isessionmanager.h \
$$PWD/api/logcontroller.h \ $$PWD/api/logcontroller.h \
$$PWD/api/rsscontroller.h \ $$PWD/api/rsscontroller.h \
@ -12,6 +11,7 @@ HEADERS += \
$$PWD/api/torrentscontroller.h \ $$PWD/api/torrentscontroller.h \
$$PWD/api/transfercontroller.h \ $$PWD/api/transfercontroller.h \
$$PWD/api/serialize/serialize_torrent.h \ $$PWD/api/serialize/serialize_torrent.h \
$$PWD/freediskspacechecker.h \
$$PWD/webapplication.h \ $$PWD/webapplication.h \
$$PWD/webui.h $$PWD/webui.h
@ -20,7 +20,6 @@ SOURCES += \
$$PWD/api/apierror.cpp \ $$PWD/api/apierror.cpp \
$$PWD/api/appcontroller.cpp \ $$PWD/api/appcontroller.cpp \
$$PWD/api/authcontroller.cpp \ $$PWD/api/authcontroller.cpp \
$$PWD/api/freediskspacechecker.cpp \
$$PWD/api/logcontroller.cpp \ $$PWD/api/logcontroller.cpp \
$$PWD/api/rsscontroller.cpp \ $$PWD/api/rsscontroller.cpp \
$$PWD/api/searchcontroller.cpp \ $$PWD/api/searchcontroller.cpp \
@ -28,6 +27,7 @@ SOURCES += \
$$PWD/api/torrentscontroller.cpp \ $$PWD/api/torrentscontroller.cpp \
$$PWD/api/transfercontroller.cpp \ $$PWD/api/transfercontroller.cpp \
$$PWD/api/serialize/serialize_torrent.cpp \ $$PWD/api/serialize/serialize_torrent.cpp \
$$PWD/freediskspacechecker.cpp \
$$PWD/webapplication.cpp \ $$PWD/webapplication.cpp \
$$PWD/webui.cpp $$PWD/webui.cpp