proxy_client: fetch stored values when retrying a failed subscription

restartListeners() retries a push subscription that failed as a refresh
("refresh": true), and the proxy answers a refresh with no values. But a
failed subscription never received the values stored on the key: they
are only delivered if a later push happens to announce them.

On Android this can drop incoming calls. The account restarts while the
device is in doze, so its subscriptions fail for lack of network; the
connection request of the call is then stored on the key, and the retry
sent on the next connectivity change does not retrieve it. The call only
rings if the proxy pushes again before the caller gives up (dhtnet waits
30 s for an answer): in one reproduction the request was processed 9 s
after the call push, in another run the call failed.

Retry a failed subscription as a full SUBSCRIBE, so the proxy answers
with the values it stores. They go through the listener value cache like
any other, so a value already received is not delivered twice.

Also fix the comment of fillBody(), which described the opposite.

Co-authored-by: Copilot (Claude Opus 5.5) <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Cyrille BéraudandCopilot committed 2026-09-26 13:07:17 -04:00
1 parent d14759eadf
commit ce8744862a
2 files changed
+10 -5

No files matched your search

+3 -1
View File
@@ -437,8 +437,10 @@ private:
/**
* Refresh a listen via a token
* @param token
* @param fetchValues ask the server to send the values it stores, for a listener
* that may have missed some (its previous subscription failed).
*/
void resubscribe(const InfoHash& key, const size_t token, Listener& listener);
void resubscribe(const InfoHash& key, const size_t token, Listener& listener, bool fetchValues = false);
/**
* If we want to use push notifications by default.
+7 -4
View File
@@ -1213,7 +1213,9 @@ DhtProxyClient::restartListeners(const asio::error_code& ec)
for (auto& search : searches_)
for (auto& listener : search.second.listeners)
if (!listener.second.opstate->ok)
resubscribe(search.first, listener.first, listener.second);
// The subscription failed: values stored on the key since then were never
// received (e.g. a connection request sent while the device had no network).
resubscribe(search.first, listener.first, listener.second, true);
return;
}
if (logger_)
@@ -1376,7 +1378,8 @@ DhtProxyClient::pushNotificationReceived([[maybe_unused]] const std::map<std::st
void
DhtProxyClient::resubscribe([[maybe_unused]] const InfoHash& key,
[[maybe_unused]] const size_t token,
[[maybe_unused]] Listener& listener)
[[maybe_unused]] Listener& listener,
[[maybe_unused]] bool fetchValues)
{
#ifdef OPENDHT_PUSH_NOTIFICATIONS
if (deviceKey_.empty())
@@ -1398,7 +1401,7 @@ DhtProxyClient::resubscribe([[maybe_unused]] const InfoHash& key,
listener.refreshSubscriberTimer->async_wait(
std::bind(&DhtProxyClient::handleResubscribe, this, std::placeholders::_1, key, token, opstate));
auto vcb = listener.cb;
sendListen(header, vcb, opstate, listener, ListenMethod::RESUBSCRIBE);
sendListen(header, vcb, opstate, listener, fetchValues ? ListenMethod::SUBSCRIBE : ListenMethod::RESUBSCRIBE);
#endif
}
@@ -1423,7 +1426,7 @@ DhtProxyClient::fillBody(bool resubscribe)
Json::Value body;
getPushRequest(body);
if (resubscribe) {
// This is the first listen, we want to retrieve previous values.
// Refreshing a subscription: the server does not send the values it stores again.
body["refresh"] = true;
}
auto content = Json::writeString(jsonBuilder_, body) + "\n";