mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-09 21:45:34 +08:00
391 lines
15 KiB
C++
391 lines
15 KiB
C++
/* Copyright (C) 2017
|
|
* swift project Community / Contributors
|
|
*
|
|
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
|
* directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated,
|
|
* or distributed except according to the terms contained in the LICENSE file.
|
|
*/
|
|
|
|
#include "networkwatchdog.h"
|
|
#include "application.h"
|
|
#include "blackmisc/logmessage.h"
|
|
#include "blackmisc/network/networkutils.h"
|
|
|
|
#include <QNetworkReply>
|
|
#include <QPointer>
|
|
|
|
using namespace BlackMisc;
|
|
using namespace BlackMisc::Network;
|
|
using namespace BlackCore::Data;
|
|
|
|
namespace BlackCore
|
|
{
|
|
namespace Db
|
|
{
|
|
const CLogCategoryList &CNetworkWatchdog::getLogCategories()
|
|
{
|
|
static const BlackMisc::CLogCategoryList cats = CContinuousWorker::getLogCategories().with(
|
|
{
|
|
CLogCategory::swiftDbWebservice(), CLogCategory::webservice(), CLogCategory::network()
|
|
});
|
|
return cats;
|
|
}
|
|
|
|
CNetworkWatchdog::CNetworkWatchdog(bool networkAccessible, QObject *owner) : CContinuousWorker(owner, "swift DB watchdog")
|
|
{
|
|
Q_ASSERT_X(owner, Q_FUNC_INFO, "Need owner (normally sApp)");
|
|
|
|
m_networkAccessible = networkAccessible;
|
|
m_internetAccessible = networkAccessible;
|
|
m_dbAccessible = networkAccessible && m_checkDbAccessibility;
|
|
|
|
m_updateTimer.setInterval(10 * 1000);
|
|
connect(&m_updateTimer, &QTimer::timeout, this, &CNetworkWatchdog::doWork);
|
|
}
|
|
|
|
void CNetworkWatchdog::setDbAccessibility(bool accessible)
|
|
{
|
|
m_dbAccessible = accessible;
|
|
m_internetAccessible = m_internetAccessible && this->isNetworkkAccessibleOrCheckDisabled();
|
|
|
|
// restart timer
|
|
QPointer<CNetworkWatchdog> myself(this);
|
|
QTimer::singleShot(0, &m_updateTimer, [ = ]
|
|
{
|
|
if (!myself) { return; }
|
|
m_updateTimer.start();
|
|
});
|
|
}
|
|
|
|
bool CNetworkWatchdog::hasWorkingSharedUrl() const
|
|
{
|
|
if (!this->isNetworkkAccessibleOrCheckDisabled()) { return false; }
|
|
return !this->getWorkingSharedUrl().isEmpty();
|
|
}
|
|
|
|
CUrl CNetworkWatchdog::getWorkingSharedUrl() const
|
|
{
|
|
if (!this->isNetworkkAccessibleOrCheckDisabled()) { return CUrl(); }
|
|
QReadLocker l(&m_lockUrl);
|
|
return m_workingSharedUrl;
|
|
}
|
|
|
|
int CNetworkWatchdog::triggerCheck()
|
|
{
|
|
if (!this->doWorkCheck()) { return false; } // senseless
|
|
if (m_checkInProgress) { return -1; }
|
|
|
|
const int n = this->getCheckCount();
|
|
const QPointer<CNetworkWatchdog> myself(this);
|
|
QTimer::singleShot(0, this, [ = ]
|
|
{
|
|
if (!myself) { return; }
|
|
this->doWork();
|
|
});
|
|
return n; // triggered
|
|
}
|
|
|
|
QString CNetworkWatchdog::getLastPingDbUrl() const
|
|
{
|
|
QReadLocker l(&m_lockUrl);
|
|
return m_lastPingUrl;
|
|
}
|
|
|
|
QString CNetworkWatchdog::getCheckInfo() const
|
|
{
|
|
static const QString info("Network accessibility check: %1 | Internet accessible: %2 (good: %3 / bad: %4), swift DB accessible: %5 (good: %6 / bad: %7) DB last ping URL: '%8' canConnect: %9ms");
|
|
const QString pUrl(this->getLastPingDbUrl());
|
|
static const QString cct = QString::number(CanConnectTimeMs);
|
|
return info.
|
|
arg(boolToEnabledDisabled(!this->isNetworkAccessibilityCheckDisabled()), boolToYesNo(this->isInternetAccessible())).arg(m_totalGoodCountInternet).arg(m_totalBadCountInternet).
|
|
arg(boolToYesNo(this->isSwiftDbAccessible())).arg(m_totalGoodCountDb).arg(m_totalBadCountDb).
|
|
arg(pUrl, cct); // cct has to be string, otherwise the % in the URL will be replaced
|
|
}
|
|
|
|
void CNetworkWatchdog::setWorkingSharedUrl(const CUrl &workingUrl)
|
|
{
|
|
QWriteLocker l(&m_lockUrl);
|
|
m_workingSharedUrl = workingUrl;
|
|
}
|
|
|
|
bool CNetworkWatchdog::isDbUrl(const CUrl &url)
|
|
{
|
|
const QString host(url.getHost());
|
|
return host == dbHost();
|
|
}
|
|
|
|
void CNetworkWatchdog::doWork()
|
|
{
|
|
if (!this->doWorkCheck()) { return; }
|
|
if (m_checkInProgress) { return; }
|
|
m_checkInProgress = true;
|
|
|
|
// lazy init
|
|
if (!this->hasWorkingSharedUrl())
|
|
{
|
|
this->initWorkingSharedUrlFromSetup();
|
|
}
|
|
|
|
// checks
|
|
do
|
|
{
|
|
const bool wasDbAvailable = m_dbAccessible;
|
|
const bool wasInternetAvailable = m_internetAccessible;
|
|
const bool networkAccessible = this->isNetworkkAccessibleOrCheckDisabled();
|
|
const CUrl testUrl(CNetworkWatchdog::dbTestUrl());
|
|
bool canConnectDb = m_checkDbAccessibility && networkAccessible;
|
|
if (canConnectDb)
|
|
{
|
|
// running here in background worker check twice
|
|
canConnectDb = CNetworkUtils::canConnect(testUrl, CanConnectTimeMs);
|
|
if (!canConnectDb)
|
|
{
|
|
canConnectDb = CNetworkUtils::canConnect(testUrl, CanConnectTimeMs);
|
|
}
|
|
}
|
|
|
|
if (m_checkDbAccessibility && m_doDetailedCheck && canConnectDb)
|
|
{
|
|
// test against real HTTP response
|
|
const bool lastHttpSuccess = m_lastClientPingSuccess; // ping result received in meantime
|
|
if (lastHttpSuccess && m_totalCheckCount % 10 == 0)
|
|
{
|
|
// seems to be OK, from time to time ping
|
|
this->pingDbClientService(CGlobalSetup::PingStarted);
|
|
}
|
|
else if (!lastHttpSuccess && m_totalCheckCount % 3 == 0)
|
|
{
|
|
// not OK, retry more frequently
|
|
this->pingDbClientService(CGlobalSetup::PingStarted, true); // force
|
|
}
|
|
canConnectDb = lastHttpSuccess;
|
|
}
|
|
|
|
bool canConnectInternet = canConnectDb;
|
|
bool checkInternetAccess = !canConnectDb;
|
|
|
|
m_dbAccessible = canConnectDb;
|
|
if (canConnectDb)
|
|
{
|
|
// DB available means internet available
|
|
m_internetAccessible = canConnectDb;
|
|
}
|
|
|
|
// check shared URL
|
|
if (!this->doWorkCheck()) { break; }
|
|
if (m_checkSharedUrl && networkAccessible)
|
|
{
|
|
if (CNetworkUtils::canConnect(this->getWorkingSharedUrl()))
|
|
{
|
|
canConnectInternet = true;
|
|
checkInternetAccess = false;
|
|
}
|
|
else
|
|
{
|
|
const CUrl sharedUrl = this->getWorkingSharedUrl();
|
|
if (!sharedUrl.isEmpty())
|
|
{
|
|
canConnectInternet = true;
|
|
checkInternetAccess = false;
|
|
this->setWorkingSharedUrl(sharedUrl);
|
|
}
|
|
}
|
|
}
|
|
|
|
// check internet access
|
|
if (!this->doWorkCheck()) { break; }
|
|
if (checkInternetAccess)
|
|
{
|
|
QString message;
|
|
static const QString testHost1("www.google.com"); // what else?
|
|
canConnectInternet = CNetworkUtils::canConnect(testHost1, 443, message, CanConnectTimeMs); // running in background worker
|
|
if (!canConnectInternet)
|
|
{
|
|
static const QString testHost2("www.microsoft.com"); // secondary test
|
|
canConnectInternet = CNetworkUtils::canConnect(testHost2, 80, message, CanConnectTimeMs); // running in background worker
|
|
}
|
|
if (canConnectInternet) { m_totalGoodCountInternet++; }
|
|
else { m_totalBadCountInternet++; }
|
|
}
|
|
m_internetAccessible = networkAccessible && canConnectInternet;
|
|
|
|
// signals
|
|
this->triggerChangedSignals(wasDbAvailable, wasInternetAvailable);
|
|
}
|
|
while (false);
|
|
|
|
m_updateTimer.start(); // restart
|
|
m_totalCheckCount++;
|
|
m_checkInProgress = false;
|
|
}
|
|
|
|
bool CNetworkWatchdog::doWorkCheck() const
|
|
{
|
|
if (!sApp || sApp->isShuttingDown()) { return false; }
|
|
if (!this->isEnabled()) { return false; }
|
|
return true;
|
|
}
|
|
|
|
void CNetworkWatchdog::setNetworkAccessibility(QNetworkAccessManager::NetworkAccessibility accessibility)
|
|
{
|
|
// avoid unnecessary signals
|
|
const int accessiblityInt = static_cast<int>(accessibility);
|
|
if (m_networkAccessibility == accessiblityInt) { return; }
|
|
if (m_disableNetworkCheck) { return; } // ignore with disabled check
|
|
|
|
// shift to thread
|
|
if (!CThreadUtils::isCurrentThreadObjectThread(this))
|
|
{
|
|
QPointer<CNetworkWatchdog> myself(this);
|
|
QTimer::singleShot(0, this, [ = ]
|
|
{
|
|
if (!sApp || sApp->isShuttingDown() || !myself) { return; }
|
|
this->setNetworkAccessibility(accessibility);
|
|
});
|
|
return;
|
|
}
|
|
|
|
// set values
|
|
m_networkAccessibility = accessiblityInt;
|
|
const bool db = m_dbAccessible;
|
|
const bool internet = m_internetAccessible;
|
|
|
|
// Intentionally rating unknown as "accessible"
|
|
if (accessibility == QNetworkAccessManager::NotAccessible)
|
|
{
|
|
m_networkAccessible = false;
|
|
m_dbAccessible = false;
|
|
m_internetAccessible = false;
|
|
this->triggerChangedSignals(db, internet);
|
|
}
|
|
else
|
|
{
|
|
m_networkAccessible = true;
|
|
const QPointer<CNetworkWatchdog> myself(this);
|
|
QTimer::singleShot(0, this, [ = ]
|
|
{
|
|
if (!myself) { return; }
|
|
this->doWork();
|
|
});
|
|
}
|
|
|
|
emit this->changedNetworkAccessible(accessibility);
|
|
}
|
|
|
|
void CNetworkWatchdog::networkConfigurationsUpdateCompleted()
|
|
{
|
|
// void
|
|
}
|
|
|
|
void CNetworkWatchdog::setOnline(bool online)
|
|
{
|
|
m_online = online;
|
|
}
|
|
|
|
void CNetworkWatchdog::gracefulShutdown()
|
|
{
|
|
this->pingDbClientService(CGlobalSetup::PingCompleteShutdown);
|
|
this->quit();
|
|
}
|
|
|
|
void CNetworkWatchdog::pingDbClientService(CGlobalSetup::PingType type, bool force)
|
|
{
|
|
if (!sApp || sApp->isShuttingDown()) { return; }
|
|
if (!force && !this->isSwiftDbAccessible()) { return; }
|
|
const CGlobalSetup gs = sApp->getGlobalSetup();
|
|
if (!gs.wasLoadedFromWeb()) { return; }
|
|
const CUrl pingUrl = gs.getDbClientPingServiceUrl(type);
|
|
sApp->getFromNetwork(pingUrl, { this, &CNetworkWatchdog::replyPingClientService });
|
|
}
|
|
|
|
bool CNetworkWatchdog::disableNetworkAccessibilityCheck(bool disable)
|
|
{
|
|
if (disable == m_disableNetworkCheck) { return false; }
|
|
m_disableNetworkCheck = disable;
|
|
return true;
|
|
}
|
|
|
|
void CNetworkWatchdog::replyPingClientService(QNetworkReply *nwReply)
|
|
{
|
|
// init and clean up
|
|
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nw(nwReply); // delete reply
|
|
const bool ok = (nw->error() == QNetworkReply::NoError);
|
|
const QString errorString = nw->errorString();
|
|
const QString url = nw->url().toString();
|
|
nw->close();
|
|
|
|
if (!sApp || sApp->isShuttingDown()) { return; }
|
|
Q_ASSERT_X(CThreadUtils::isCurrentThreadObjectThread(this), Q_FUNC_INFO, "Wrong thread");
|
|
|
|
m_lastClientPingSuccess = ok;
|
|
{
|
|
QWriteLocker l(&m_lockUrl);
|
|
m_lastPingUrl = url;
|
|
}
|
|
|
|
if (ok)
|
|
{
|
|
// be a little less verbose
|
|
if ((m_totalGoodCountDb % 5 == 0) || m_consecutivePingBadCount > 0)
|
|
{
|
|
CLogMessage(this).info(u"Watchdog pinged '%1'") << url;
|
|
}
|
|
m_totalGoodCountDb++;
|
|
m_consecutivePingBadCount = 0;
|
|
}
|
|
else
|
|
{
|
|
m_totalBadCountDb++;
|
|
m_consecutivePingBadCount++;
|
|
if (m_logOwnMessages)
|
|
{
|
|
CStatusMessage(this).warning(u"Watchdog ping failed, error: '%1', total good/bad DB counts: %2/%3") << errorString << m_totalGoodCountDb << m_totalBadCountDb;
|
|
}
|
|
}
|
|
this->setDbAccessibility(ok);
|
|
}
|
|
|
|
void CNetworkWatchdog::triggerChangedSignals(bool oldDbAccessible, bool oldInternetAccessible)
|
|
{
|
|
if (!this->doWorkCheck()) { return; }
|
|
|
|
if (oldDbAccessible != m_dbAccessible)
|
|
{
|
|
const CUrl testUrl(this->dbTestUrl());
|
|
emit this->changedSwiftDbAccessibility(m_dbAccessible, testUrl);
|
|
}
|
|
if (oldInternetAccessible != m_internetAccessible)
|
|
{
|
|
emit this->changedInternetAccessibility(m_internetAccessible);
|
|
}
|
|
}
|
|
|
|
void CNetworkWatchdog::initWorkingSharedUrlFromSetup()
|
|
{
|
|
const CUrl workingUrl(CNetworkWatchdog::workingSharedUrlFromSetup()); // takes long
|
|
this->setWorkingSharedUrl(workingUrl);
|
|
}
|
|
|
|
CUrl CNetworkWatchdog::dbTestUrl()
|
|
{
|
|
// requires global setup to be read
|
|
if (!sApp || sApp->isShuttingDown()) { return CUrl(); }
|
|
const CUrl testUrl(sApp->getGlobalSetup().getDbHomePageUrl());
|
|
return testUrl;
|
|
}
|
|
|
|
QString CNetworkWatchdog::dbHost()
|
|
{
|
|
const QString host = dbTestUrl().getHost();
|
|
return host;
|
|
}
|
|
|
|
CUrl CNetworkWatchdog::workingSharedUrlFromSetup()
|
|
{
|
|
const CUrlList urls(sApp->getGlobalSetup().getSwiftSharedUrls());
|
|
CFailoverUrlList failoverUrls(urls);
|
|
return failoverUrls.getRandomWorkingUrl(2, CanConnectTimeMs); // uses CNetworkUtils::canConnect
|
|
}
|
|
} // ns
|
|
} // ns
|