Handle redirections more nicely

* Log redirections in accordance with logging settings
* Load self-signed certificate as needed
This commit is contained in:
Martchus 2023-12-30 20:11:34 +01:00
parent ddd4f6b411
commit c1284331be
4 changed files with 24 additions and 4 deletions

View File

@ -778,13 +778,13 @@ void SyncthingConnection::continueConnecting()
* only do the cleanup of previous certificates but not emit any errors. * only do the cleanup of previous certificates but not emit any errors.
* \returns Returns whether a certificate could be loaded. * \returns Returns whether a certificate could be loaded.
*/ */
bool SyncthingConnection::loadSelfSignedCertificate() bool SyncthingConnection::loadSelfSignedCertificate(const QUrl &url)
{ {
// ensure current exceptions for self-signed certificates are cleared // ensure current exceptions for self-signed certificates are cleared
m_expectedSslErrors.clear(); m_expectedSslErrors.clear();
// not required when not using secure connection // not required when not using secure connection
const QUrl syncthingUrl(m_syncthingUrl); const auto syncthingUrl = url.isEmpty() ? m_syncthingUrl : url;
if (!syncthingUrl.scheme().endsWith(QChar('s'))) { if (!syncthingUrl.scheme().endsWith(QChar('s'))) {
return false; return false;
} }

View File

@ -180,7 +180,7 @@ public:
const QList<QSslError> &expectedSslErrors() const; const QList<QSslError> &expectedSslErrors() const;
public Q_SLOTS: public Q_SLOTS:
bool loadSelfSignedCertificate(); bool loadSelfSignedCertificate(const QUrl &url = QUrl());
bool applySettings(Data::SyncthingConnectionSettings &connectionSettings); bool applySettings(Data::SyncthingConnectionSettings &connectionSettings);
// methods to initiate/close connection // methods to initiate/close connection
@ -337,6 +337,7 @@ private Q_SLOTS:
void handleFatalConnectionError(); void handleFatalConnectionError();
void handleAdditionalRequestCanceled(); void handleAdditionalRequestCanceled();
void handleSslErrors(const QList<QSslError> &errors); void handleSslErrors(const QList<QSslError> &errors);
void handleRedirection(const QUrl &url);
void recalculateStatus(); void recalculateStatus();
QString configPath() const; QString configPath() const;
QByteArray changeConfigVerb() const; QByteArray changeConfigVerb() const;

View File

@ -63,6 +63,7 @@ QNetworkReply *SyncthingConnection::requestData(const QString &path, const QUrlQ
#ifndef LIB_SYNCTHING_CONNECTOR_CONNECTION_MOCKED #ifndef LIB_SYNCTHING_CONNECTOR_CONNECTION_MOCKED
auto *const reply = networkAccessManager().get(prepareRequest(path, query, rest, longPolling)); auto *const reply = networkAccessManager().get(prepareRequest(path, query, rest, longPolling));
QObject::connect(reply, &QNetworkReply::sslErrors, this, &SyncthingConnection::handleSslErrors); QObject::connect(reply, &QNetworkReply::sslErrors, this, &SyncthingConnection::handleSslErrors);
QObject::connect(reply, &QNetworkReply::redirected, this, &SyncthingConnection::handleRedirection);
if (loggingFlags() & SyncthingConnectionLoggingFlags::ApiCalls) { if (loggingFlags() & SyncthingConnectionLoggingFlags::ApiCalls) {
cerr << Phrases::Info << "Querying API: GET " << reply->url().toString().toStdString() << Phrases::EndFlush; cerr << Phrases::Info << "Querying API: GET " << reply->url().toString().toStdString() << Phrases::EndFlush;
} }
@ -188,6 +189,24 @@ void SyncthingConnection::handleSslErrors(const QList<QSslError> &errors)
} }
} }
/*!
* \brief Handles redirections.
* \remarks The redirect policy will decide whether to follow the redirection or not.
* This function merely handles logging and loading the certificate in case this
* hasn't happened before (e.g. a redirection from http to https).
*/
void SyncthingConnection::handleRedirection(const QUrl &url)
{
if (m_loggingFlags & SyncthingConnectionLoggingFlags::ApiReplies) {
const auto urlStr = url.toString().toUtf8();
cerr << Phrases::Info << "Got redirected to: " << std::string_view(urlStr.data(), static_cast<std::string_view::size_type>(urlStr.size()))
<< Phrases::EndFlush;
}
if (m_expectedSslErrors.isEmpty() && url.scheme().endsWith(QChar('s'))) {
loadSelfSignedCertificate(url);
}
}
/*! /*!
* \brief Handles the specified \a reply; invoked by the prepareReply() functions. * \brief Handles the specified \a reply; invoked by the prepareReply() functions.
*/ */

View File

@ -188,7 +188,7 @@ void MiscTests::testConnectionSettingsAndLoadingSelfSignedCert()
bool errorOccured = false; bool errorOccured = false;
const function<void(const QString &)> errorHandler const function<void(const QString &)> errorHandler
= [&errorOccured](const QString &message) { errorOccured |= message == QStringLiteral("Unable to load certificate used by Syncthing."); }; = [&errorOccured](const QString &message) { errorOccured |= message == QStringLiteral("Unable to load certificate used by Syncthing."); };
waitForSignals(bind(&SyncthingConnection::loadSelfSignedCertificate, &connection), 1, waitForSignals(bind(&SyncthingConnection::loadSelfSignedCertificate, &connection, QUrl()), 1,
signalInfo(&connection, &SyncthingConnection::error, errorHandler, &errorOccured)); signalInfo(&connection, &SyncthingConnection::error, errorHandler, &errorOccured));
settings.expectedSslErrors.clear(); settings.expectedSslErrors.clear();
CPPUNIT_ASSERT(!connection.applySettings(settings)); CPPUNIT_ASSERT(!connection.applySettings(settings));