Compare commits

...
Author SHA1 Message Date
Cyrille BéraudandCopilot 87bb2842ce proxy: include priority field in push notification JSON payload
The HTTP Urgency header set on WebPush requests is not exposed to
UnifiedPush Android clients by the connector library. Include a
"priority" field ("high" or "normal") directly in the JSON body
so that clients can determine whether a push notification requires
immediate wake-up processing or can be deferred until the next
natural activity.

This complements the daemon-side priority assignment (Gerrit #34059)
and allows UnifiedPush (F-Droid) builds to benefit from the same
battery savings as FCM builds.

Change-Id: Id316ff89e64e59d1b224f5b4e14da6b36dc62c95
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-20 18:55:12 -04:00
Cyrille BéraudandCopilot ba753dde6f proxy: fix resource leaks and improve mobile battery life
Server: Reduce HTTP idle connection timeout from ~24 days
(numeric_limits<int>::max() milliseconds) to 1 hour. Dead mobile
clients are now cleaned up promptly instead of holding server
resources for weeks.

Client: Add exponential backoff for proxy reconnection when
disconnected (1 min -> 2 -> 4 -> 8 -> 16 -> 30 min cap). Previously
used a fixed 1-minute retry which drained battery on flaky mobile
networks. Reset backoff on successful connection or explicit
connectivity change (WiFi/cellular switch).

Client: Add small random jitter (0-2s) before resubscribing all
listeners on reconnection to avoid burst of requests to the server.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Change-Id: Ic29c4a0b2850997db9e559f01146024f41190a5c
2026-05-16 18:47:07 -04:00
4 changed files with 30 additions and 5 deletions

No files matched your search

+1
View File
@@ -69,3 +69,4 @@ build-*/
install/
vcpkg_installed/
jamid.log
+4
View File
@@ -429,6 +429,10 @@ private:
std::unique_ptr<asio::steady_timer> nextProxyConfirmationTimer_;
std::unique_ptr<asio::steady_timer> listenerRestartTimer_;
// Exponential backoff for proxy reconnection (1 min → 2 → 4 → ... → 30 min)
std::chrono::minutes proxyRetryDelay_ {1};
static constexpr std::chrono::minutes MAX_PROXY_RETRY_DELAY {30};
/**
* Relaunch LISTEN requests if the client disconnect/reconnect.
*/
+14 -2
View File
@@ -782,7 +782,10 @@ DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos, const sa_family_t fa
if (newStatus == NodeStatus::Connected) {
if (oldStatus == NodeStatus::Disconnected || oldStatus == NodeStatus::Connecting || launchConnectedCbs_) {
launchConnectedCbs_ = false;
listenerRestartTimer_->expires_at(std::chrono::steady_clock::now());
// Small random delay (0-2s) to avoid resubscription burst
auto jitter = std::chrono::milliseconds(
std::rand() % 2000);
listenerRestartTimer_->expires_at(std::chrono::steady_clock::now() + jitter);
listenerRestartTimer_->async_wait(std::bind(&DhtProxyClient::restartListeners, this, std::placeholders::_1));
if (not onConnectCallbacks_.empty()) {
std::lock_guard lock(lockCallbacks_);
@@ -794,11 +797,18 @@ DhtProxyClient::onProxyInfos(const Json::Value& proxyInfos, const sa_family_t fa
});
}
}
// Reset backoff on successful connection
proxyRetryDelay_ = std::chrono::minutes(1);
nextProxyConfirmationTimer_->expires_at(std::chrono::steady_clock::now() + std::chrono::minutes(15));
nextProxyConfirmationTimer_->async_wait(
std::bind(&DhtProxyClient::handleProxyConfirm, this, std::placeholders::_1));
} else if (newStatus == NodeStatus::Disconnected) {
nextProxyConfirmationTimer_->expires_at(std::chrono::steady_clock::now() + std::chrono::minutes(1));
// Exponential backoff: 1 → 2 → 4 → 8 → 16 → 30 min (capped)
auto delay = proxyRetryDelay_;
proxyRetryDelay_ = std::min(proxyRetryDelay_ * 2, MAX_PROXY_RETRY_DELAY);
if (logger_)
logger_->warn("[proxy:client] disconnected, retrying in {} min", delay.count());
nextProxyConfirmationTimer_->expires_at(std::chrono::steady_clock::now() + delay);
nextProxyConfirmationTimer_->async_wait(
std::bind(&DhtProxyClient::handleProxyConfirm, this, std::placeholders::_1));
}
@@ -1166,6 +1176,8 @@ DhtProxyClient::getConnectivityStatus()
{
if (logger_)
logger_->d("[proxy:client] [connectivity] get status");
// Reset backoff on explicit connectivity change (e.g., WiFi/cellular switch)
proxyRetryDelay_ = std::chrono::minutes(1);
if (!isDestroying_)
getProxyInfos();
}
+11 -3
View File
@@ -594,8 +594,10 @@ DhtProxyServer::addServerSettings(ServerSettings& settings, const unsigned int m
settings.logger(logger_);
settings.protocol(restinio::asio_ns::ip::tcp::v6());
settings.request_handler(createRestRouter());
// time limits // ~ 0.8 month
std::chrono::milliseconds timeout_request(std::numeric_limits<int>::max());
// time limits
// Use 1-hour timeout for idle connections so that dead mobile clients
// are cleaned up promptly instead of lingering for ~24 days.
constexpr auto timeout_request = std::chrono::hours(1);
settings.read_next_http_message_timelimit(timeout_request);
settings.write_http_response_timelimit(60s);
settings.handle_request_timeout(timeout_request);
@@ -1189,7 +1191,13 @@ DhtProxyServer::handlePushListen(const InfoHash& infoHash,
minPriority,
values.size());
sendPushNotification(pushToken, std::move(json), type, !expired and minPriority == 0, topic);
// Include priority in the JSON payload so that UnifiedPush clients
// (which don't have access to the HTTP Urgency header) can determine
// whether a push requires immediate processing or can be deferred.
bool highPriority = !expired and minPriority == 0;
json["priority"] = highPriority ? "high" : "normal";
sendPushNotification(pushToken, std::move(json), type, highPriority, topic);
return true;
}