diff --git a/syncthingconnector/syncthingconnection.cpp b/syncthingconnector/syncthingconnection.cpp index 73f0039..c189ea3 100644 --- a/syncthingconnector/syncthingconnection.cpp +++ b/syncthingconnector/syncthingconnection.cpp @@ -83,6 +83,7 @@ SyncthingConnection::SyncthingConnection( , m_lastDiskEventId(0) , m_autoReconnectTries(0) , m_requestTimeout(SyncthingConnectionSettings::defaultRequestTimeout) + , m_longPollingTimeout(SyncthingConnectionSettings::defaultLongPollingTimeout) , m_totalIncomingTraffic(unknownTraffic) , m_totalOutgoingTraffic(unknownTraffic) , m_totalIncomingRate(0.0) @@ -857,6 +858,7 @@ bool SyncthingConnection::applySettings(SyncthingConnectionSettings &connectionS setErrorsPollInterval(connectionSettings.errorsPollInterval); setAutoReconnectInterval(connectionSettings.reconnectInterval); setRequestTimeout(connectionSettings.requestTimeout); + setLongPollingTimeout(connectionSettings.longPollingTimeout); setStatusComputionFlags(connectionSettings.statusComputionFlags); return reconnectRequired; diff --git a/syncthingconnector/syncthingconnection.h b/syncthingconnector/syncthingconnection.h index f8e0ac3..b6e7289 100644 --- a/syncthingconnector/syncthingconnection.h +++ b/syncthingconnector/syncthingconnection.h @@ -73,6 +73,7 @@ class LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnection : public QObject { Q_PROPERTY(int devStatsPollInterval READ devStatsPollInterval WRITE setDevStatsPollInterval) Q_PROPERTY(bool recordFileChanges READ recordFileChanges WRITE setRecordFileChanges) Q_PROPERTY(int requestTimeout READ requestTimeout WRITE setRequestTimeout) + Q_PROPERTY(int longPollingTimeout READ longPollingTimeout WRITE setLongPollingTimeout) Q_PROPERTY(QString myId READ myId NOTIFY myIdChanged) Q_PROPERTY(QString tilde READ tilde NOTIFY tildeChanged) Q_PROPERTY(QString pathSeparator READ pathSeparator NOTIFY tildeChanged) @@ -141,6 +142,8 @@ public: void setRecordFileChanges(bool recordFileChanges); int requestTimeout() const; void setRequestTimeout(int requestTimeout); + int longPollingTimeout() const; + void setLongPollingTimeout(int longPollingTimeout); // getter for information retrieved from Syncthing const QString &configDir() const; @@ -345,8 +348,8 @@ private: QNetworkReply *reply; QByteArray response; }; - QNetworkRequest prepareRequest(const QString &path, const QUrlQuery &query, bool rest = true, bool noTimeout = false); - QNetworkReply *requestData(const QString &path, const QUrlQuery &query, bool rest = true, bool noTimeout = false); + QNetworkRequest prepareRequest(const QString &path, const QUrlQuery &query, bool rest = true, bool longPolling = false); + QNetworkReply *requestData(const QString &path, const QUrlQuery &query, bool rest = true, bool longPolling = false); QNetworkReply *postData(const QString &path, const QUrlQuery &query, const QByteArray &data = QByteArray()); QNetworkReply *sendData(const QByteArray &verb, const QString &path, const QUrlQuery &query, const QByteArray &data = QByteArray()); Reply prepareReply(bool readData = true, bool handleAborting = true); @@ -382,6 +385,7 @@ private: QTimer m_autoReconnectTimer; unsigned int m_autoReconnectTries; int m_requestTimeout; + int m_longPollingTimeout; QString m_configDir; QString m_myId; QString m_tilde; @@ -739,6 +743,25 @@ inline void SyncthingConnection::setRequestTimeout(int requestTimeout) m_requestTimeout = requestTimeout; } +/*! + * \brief Returns the transfer timeout for long polling requests (event APIs) in milliseconds. + * \sa QNetworkRequest::transferTimeout() + */ +inline int SyncthingConnection::longPollingTimeout() const +{ + return m_longPollingTimeout; +} + +/*! + * \brief Sets the transfer timeout for long polling requests (event APIs) in milliseconds. + * \remarks Existing requests are not affected. Only effective when compiled against Qt 5.15 or higher. + * \sa QNetworkRequest::setTransferTimeout() + */ +inline void SyncthingConnection::setLongPollingTimeout(int longPollingTimeout) +{ + m_longPollingTimeout = longPollingTimeout; +} + /*! * \brief Returns what information is considered to compute the overall status returned by status(). */ diff --git a/syncthingconnector/syncthingconnection_requests.cpp b/syncthingconnector/syncthingconnection_requests.cpp index 70fc3f2..e489943 100644 --- a/syncthingconnector/syncthingconnection_requests.cpp +++ b/syncthingconnector/syncthingconnection_requests.cpp @@ -33,7 +33,7 @@ namespace Data { /*! * \brief Prepares a request for the specified \a path and \a query. */ -QNetworkRequest SyncthingConnection::prepareRequest(const QString &path, const QUrlQuery &query, bool rest, bool noTimeout) +QNetworkRequest SyncthingConnection::prepareRequest(const QString &path, const QUrlQuery &query, bool rest, bool longPolling) { QUrl url(m_syncthingUrl); url.setPath(rest ? (url.path() % QStringLiteral("/rest/") % path) : (url.path() + path)); @@ -49,7 +49,8 @@ QNetworkRequest SyncthingConnection::prepareRequest(const QString &path, const Q request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); #endif #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) - request.setTransferTimeout(noTimeout ? 0 : m_requestTimeout); + // give it a few seconds more time than the actual long polling interval set via the timeout query parameter + request.setTransferTimeout(longPolling ? (m_longPollingTimeout ? m_longPollingTimeout + 5000 : 0) : m_requestTimeout); #endif return request; } @@ -57,16 +58,17 @@ QNetworkRequest SyncthingConnection::prepareRequest(const QString &path, const Q /*! * \brief Requests asynchronously data using the rest API. */ -QNetworkReply *SyncthingConnection::requestData(const QString &path, const QUrlQuery &query, bool rest, bool noTimeout) +QNetworkReply *SyncthingConnection::requestData(const QString &path, const QUrlQuery &query, bool rest, bool longPolling) { #ifndef LIB_SYNCTHING_CONNECTOR_CONNECTION_MOCKED - auto *const reply = networkAccessManager().get(prepareRequest(path, query, rest, noTimeout)); + auto *const reply = networkAccessManager().get(prepareRequest(path, query, rest, longPolling)); QObject::connect(reply, &QNetworkReply::sslErrors, this, &SyncthingConnection::handleSslErrors); if (loggingFlags() & SyncthingConnectionLoggingFlags::ApiCalls) { cerr << Phrases::Info << "Querying API: GET " << reply->url().toString().toStdString() << Phrases::EndFlush; } return reply; #else + Q_UNUSED(longPolling) return MockedReply::forRequest(QStringLiteral("GET"), path, query, rest); #endif } @@ -1709,6 +1711,8 @@ void SyncthingConnection::requestEvents() // force to return immediately after the first call if (!m_hasEvents) { query.addQueryItem(QStringLiteral("timeout"), QStringLiteral("0")); + } else if (m_longPollingTimeout) { + query.addQueryItem(QStringLiteral("timeout"), QString::number(m_longPollingTimeout / 1000)); } QObject::connect(m_eventsReply = requestData(QStringLiteral("events"), query, true, m_hasEvents), &QNetworkReply::finished, this, &SyncthingConnection::readEvents); @@ -2315,6 +2319,8 @@ void SyncthingConnection::requestDiskEvents(int limit) // force to return immediately after the first call if (!m_hasDiskEvents) { query.addQueryItem(QStringLiteral("timeout"), QStringLiteral("0")); + } else if (m_longPollingTimeout) { + query.addQueryItem(QStringLiteral("timeout"), QString::number(m_longPollingTimeout / 1000)); } QObject::connect(m_diskEventsReply = requestData(QStringLiteral("events/disk"), query, true, m_hasDiskEvents), &QNetworkReply::finished, this, &SyncthingConnection::readDiskEvents); diff --git a/syncthingconnector/syncthingconnectionsettings.h b/syncthingconnector/syncthingconnectionsettings.h index 6a9b7cb..cf1c1ab 100644 --- a/syncthingconnector/syncthingconnectionsettings.h +++ b/syncthingconnector/syncthingconnectionsettings.h @@ -44,6 +44,7 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnectionSettings { int errorsPollInterval = defaultErrorsPollInterval; int reconnectInterval = defaultReconnectInterval; int requestTimeout = defaultRequestTimeout; + int longPollingTimeout = defaultLongPollingTimeout; QString httpsCertPath; QList expectedSslErrors; SyncthingStatusComputionFlags statusComputionFlags = SyncthingStatusComputionFlags::Default; @@ -55,6 +56,7 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnectionSettings { static constexpr int defaultErrorsPollInterval = 30000; static constexpr int defaultReconnectInterval = 30000; static constexpr int defaultRequestTimeout = 0; + static constexpr int defaultLongPollingTimeout = 0; }; } // namespace Data diff --git a/syncthingwidgets/settings/connectionoptionpage.ui b/syncthingwidgets/settings/connectionoptionpage.ui index eff4c8c..dde9f2f 100644 --- a/syncthingwidgets/settings/connectionoptionpage.ui +++ b/syncthingwidgets/settings/connectionoptionpage.ui @@ -305,7 +305,7 @@ - + Poll interval @@ -315,7 +315,7 @@ - + 10 @@ -455,7 +455,7 @@ - + Whether to connect automatically on startup. This setting might be overruled by systemd and launcher settings. @@ -465,7 +465,7 @@ - + Overall status @@ -475,7 +475,7 @@ - + 2 @@ -520,21 +520,21 @@ - + Current status - + disconnected - + @@ -567,6 +567,9 @@ + + The timeout for normal requests to Syncthing's API. Set to zero to enforce no timeout. + Transfer timeout @@ -614,6 +617,32 @@ + + + + The timeout for long-polling requests to Syncthing's event API. Set to zero to use Syncthing's default timeout and no timeout being enforced from Syncthing Tray's side. + + + Long polling int. + + + + + + + Syncthing's default with no timeout + + + ms + + + 1000000000 + + + 60000 + + + diff --git a/syncthingwidgets/settings/settings.cpp b/syncthingwidgets/settings/settings.cpp index 26761fb..bebc219 100644 --- a/syncthingwidgets/settings/settings.cpp +++ b/syncthingwidgets/settings/settings.cpp @@ -310,6 +310,8 @@ bool restore() connectionSettings->reconnectInterval = settings.value(QStringLiteral("reconnectInterval"), connectionSettings->reconnectInterval).toInt(); connectionSettings->requestTimeout = settings.value(QStringLiteral("requestTimeout"), connectionSettings->requestTimeout).toInt(); + connectionSettings->longPollingTimeout + = settings.value(QStringLiteral("longPollingTimeout"), connectionSettings->longPollingTimeout).toInt(); connectionSettings->autoConnect = settings.value(QStringLiteral("autoConnect"), connectionSettings->autoConnect).toBool(); const auto statusComputionFlags = settings.value(QStringLiteral("statusComputionFlags"), QVariant::fromValue(static_cast(connectionSettings->statusComputionFlags))); @@ -461,6 +463,7 @@ bool save() settings.setValue(QStringLiteral("errorsPollInterval"), connectionSettings->errorsPollInterval); settings.setValue(QStringLiteral("reconnectInterval"), connectionSettings->reconnectInterval); settings.setValue(QStringLiteral("requestTimeout"), connectionSettings->requestTimeout); + settings.setValue(QStringLiteral("longPollingTimeout"), connectionSettings->longPollingTimeout); settings.setValue(QStringLiteral("autoConnect"), connectionSettings->autoConnect); settings.setValue(QStringLiteral("statusComputionFlags"), QVariant::fromValue(static_cast>(connectionSettings->statusComputionFlags))); diff --git a/syncthingwidgets/settings/settingsdialog.cpp b/syncthingwidgets/settings/settingsdialog.cpp index 0df7079..ed785a2 100644 --- a/syncthingwidgets/settings/settingsdialog.cpp +++ b/syncthingwidgets/settings/settingsdialog.cpp @@ -214,6 +214,7 @@ bool ConnectionOptionPage::showConnectionSettings(int index) ui()->apiKeyLineEdit->setText(connectionSettings.apiKey); ui()->certPathSelection->lineEdit()->setText(connectionSettings.httpsCertPath); ui()->timeoutSpinBox->setValue(connectionSettings.requestTimeout); + ui()->longPollingSpinBox->setValue(connectionSettings.longPollingTimeout); ui()->pollTrafficSpinBox->setValue(connectionSettings.trafficPollInterval); ui()->pollDevStatsSpinBox->setValue(connectionSettings.devStatsPollInterval); ui()->pollErrorsSpinBox->setValue(connectionSettings.errorsPollInterval); @@ -240,6 +241,7 @@ bool ConnectionOptionPage::cacheCurrentSettings(bool applying) connectionSettings.expectedSslErrors.clear(); connectionSettings.httpsCertPath = ui()->certPathSelection->lineEdit()->text(); connectionSettings.requestTimeout = ui()->timeoutSpinBox->value(); + connectionSettings.longPollingTimeout = ui()->longPollingSpinBox->value(); connectionSettings.trafficPollInterval = ui()->pollTrafficSpinBox->value(); connectionSettings.devStatsPollInterval = ui()->pollDevStatsSpinBox->value(); connectionSettings.errorsPollInterval = ui()->pollErrorsSpinBox->value(); @@ -364,9 +366,9 @@ void ConnectionOptionPage::toggleAdvancedSettings(bool show) return; } for (auto *const widget : std::initializer_list{ ui()->authLabel, ui()->authCheckBox, ui()->userNameLabel, ui()->userNameLineEdit, - ui()->passwordLabel, ui()->passwordLineEdit, ui()->timeoutLabel, ui()->timeoutSpinBox, ui()->pollLabel, ui()->pollDevStatsLabel, - ui()->pollDevStatsSpinBox, ui()->pollErrorsLabel, ui()->pollErrorsSpinBox, ui()->pollTrafficLabel, ui()->pollTrafficSpinBox, - ui()->reconnectLabel, ui()->reconnectSpinBox }) { + ui()->passwordLabel, ui()->passwordLineEdit, ui()->timeoutLabel, ui()->timeoutSpinBox, ui()->longPollingLabel, ui()->longPollingSpinBox, + ui()->pollLabel, ui()->pollDevStatsLabel, ui()->pollDevStatsSpinBox, ui()->pollErrorsLabel, ui()->pollErrorsSpinBox, + ui()->pollTrafficLabel, ui()->pollTrafficSpinBox, ui()->reconnectLabel, ui()->reconnectSpinBox }) { widget->setVisible(show); } }