Commit Graph
3589 Commits
Author SHA1 Message Date
Cyrille BéraudandCopilot ce8744862a 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>
2026-09-26 13:07:17 -04:00
Cyrille BéraudandCopilot d14759eadf tests: cover values missed by a failed push subscription
A push subscription that fails (typically while the device has no
network) is retried by restartListeners() on the next connectivity
change. The values stored on the key meanwhile were never received, so
the retry must ask the proxy for them, i.e. not be sent as a refresh.

Co-authored-by: Copilot (Claude Opus 5.5) <223556219+Copilot@users.noreply.github.com>
2026-09-26 13:06:45 -04:00
Adrien Beraud 621b44c8e8 Revert "disable windows wheel for now"
This reverts commit fc6edb4d20.
v4.4.1
2026-09-23 14:42:57 -04:00
Adrien Beraud e1c088f2e4 build: bump version to 4.4.1 2026-09-23 14:38:36 -04:00
Adrien Beraud a6e0e441ac crypto: use standard unsigned int* for gnutls_ocsp_resp_get_single 2026-09-22 11:31:45 -04:00
Adrien Beraud dcc2ba3492 crypto: use gnutls_ocsp_cert_status_t* in gnutls_ocsp_resp_get_single 2026-09-22 11:31:45 -04:00
Adrien Beraud ccf00d6b09 ports: update shiftmedia-libgnutls to f76d780a6 2026-09-22 11:31:45 -04:00
Adrien Beraud d8250574fc ports: use ForcedIncludeFiles for gnutls_msvc_fixes.h in shiftmedia-libgnutls 2026-09-22 11:31:45 -04:00
Adrien Beraud 0d698c76e8 ports: update shiftmedia-libgnutls to 9ebeb826d 2026-09-22 11:31:45 -04:00
Adrien Beraud e4e3b4e73b ports: set LanguageStandard_C to stdc11 in shiftmedia-libgnutls 2026-09-22 11:31:45 -04:00
Adrien Beraud b26d622f5d ports: update shiftmedia-libgnutls to 37840cfa5 2026-09-22 11:31:45 -04:00
Adrien Beraud 78694751bd ports: update shiftmedia-libgnutls to 4ab78d57e 2026-09-22 11:31:45 -04:00
Adrien Beraud f9b90c5c1c ports: update shiftmedia-libgnutls to 5a5a9dd95 2026-09-22 11:31:45 -04:00
Adrien Beraud 56b848bf47 ports: fix savoirfairelinux/gnutls commit hash and SHA512 in shiftmedia-libgnutls 2026-09-22 11:31:45 -04:00
Adrien Beraud dd875230c3 ports: update shiftmedia-libgnutls to 90eb686e8 2026-09-22 11:31:45 -04:00
Adrien Beraud de1d428e63 ports: use gitlab gnulib mirror archive in shiftmedia-libgnutls 2026-09-22 11:31:45 -04:00
Adrien Beraud 55e3a6514f ports: fix gnulib snapshot SHA512 hash in shiftmedia-libgnutls 2026-09-22 11:31:45 -04:00
Adrien Beraud f4bbef210c ports: fix savoirfairelinux/gnutls archive SHA512 hash in shiftmedia-libgnutls 2026-09-22 11:31:45 -04:00
Adrien Beraud 2c1470cef6 vcpkg: add overlay port for savoirfairelinux/gnutls on Windows MSVC 2026-09-22 11:31:45 -04:00
Adrien Beraud adb6aa57e6 update vcpkg 2026-09-22 11:31:45 -04:00
Adrien Beraud acd0abdb70 ci: fix vcpkg cache keys across workflows and remove manual version string 2026-09-22 11:31:45 -04:00
Cyrille BéraudandCopilot b9c8f5e0fd runner: complete pushes received while stopped
Resolve idle-runner push futures immediately under the operation queue lock. A stopped runner has no loop left to execute the queued promise, otherwise consumers can wait indefinitely.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-21 16:02:47 -04:00
Cyrille BéraudandCopilot 18fedf431f tests: cover pushes received by stopped runners
Require immediate completion before run and after join, including a burst of notifications. Keep active-runner coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-21 16:02:47 -04:00
Adrien Beraud fc6edb4d20 disable windows wheel for now v4.4.0.1 2026-09-19 22:09:44 -04:00
Adrien Beraud d1093c77a3 ci: use libmsgpack-cxx-dev on Debian 13 2026-09-19 21:56:06 -04:00
Adrien Beraud b543f1e3cb update vcpkg to 2026.06.01 2026-09-19 21:10:44 -04:00
Adrien Beraud be210e27da update version to 4.4.0 v4.4.0 2026-09-04 16:46:41 -04:00
Adrien Beraud f7243fca37 value_cache: only emit an edition when the content actually changed
ValueCache re-emitted a known value as an edition on a higher seq alone,
while OpValueCache requires the content to differ as well. Since
Value::operator== ignores seq, identical content re-announced with a
bumped seq was re-emitted as an add: a source-counting OpValueCache
then counted the same node twice and kept the value after the node
expired or left the search. Apply the same rule at both levels.
2026-09-04 15:44:35 -04:00
Adrien Beraud 1f6efd5736 tests: cover same-content higher-seq re-announce in ValueCache
Value::operator== ignores seq, so a node re-announcing identical content
with a bumped seq is the same value refreshed, not an edition. ValueCache
must not re-emit it: a source-counting OpValueCache above would count the
node twice and keep the value after the node is gone.
2026-09-04 15:44:35 -04:00
Adrien Beraud 54fbad3a7e op_cache: ignore expiry of versions that were never counted
onValuesAdded ignores a different payload that does not carry a higher
seq (e.g. the same signed content re-signed with a randomized
signature), but onValuesExpired only skipped strictly older versions.
The uncounted source could therefore decrement the refcount and expire
a value still held by others. Apply the same rule on both sides.
2026-09-04 15:18:35 -04:00
Adrien Beraud 1315489a7b tests: cover expiry of an uncounted same-seq version in OpValueCache
A source reporting a different payload with the same seq is ignored on
add and not counted; its later expiration must not release the value
still held by the counted sources.
2026-09-04 15:18:35 -04:00
Adrien Beraud c189fa0e82 proxy_server: detach pending push requests before destruction
~DhtProxyServer destroyed requests_ whose ~http::Request notifies DONE;
the handler registered by sendPushNotification then erased from the map
being destroyed, corrupting the heap (double free under ASan) whenever a
push request was still pending, e.g. an unreachable push server. Detach
the state-change callbacks and release the requests before the members
are destroyed.
2026-09-04 15:18:24 -04:00
Adrien Beraud 3eae03289e storage: re-storing identical content is a refresh, not an edition
Storage::store reported edited_values=1 whenever the stored Sp<Value>
differed from the incoming one, even with equal content. A local put()
of an already-stored value was therefore announced to local listeners
as a new value a second time, unbalancing listen caches that count how
many sources hold a value. Only report an edition when the content
actually changed; the expiration is still refreshed.
2026-09-04 15:18:24 -04:00
Adrien Béraud e5e5a45b91 op_cache, proxy_client: guard SearchCache::get and push refresh expiry
SearchCache::get called the GetCallback unconditionally; Dht::query
passes none, which threw bad_function_call as soon as a listen cache
existed for the key. Return false without a callback so the network
path runs. A callback returning false means the consumer has enough
values, not a failure: report done(true) as the network path does.

DhtProxyClient push refresh expired every previously cached value once
the refresh get completed, including values that were just re-delivered
or edited, producing phantom expire callbacks. Track the ids received by
the get and only expire cached values missing from it, and skip the
reconciliation when the get failed so a transient error does not expire
values still present on the DHT.
2026-09-04 15:18:24 -04:00
Adrien Beraud 7a71beb892 value_cache: detect editions by increasing seq, like OpValueCache
ValueCache emitted any content change of a known value as an edition
while OpValueCache only accepts a higher seq. A node reporting an older
version made ValueCache replace its copy and re-emit versions that were
then dropped or, with source counting, double counted downstream. Use
the same rule at both levels.
2026-09-04 15:18:24 -04:00
Adrien Béraud 74a894229f value_cache, op_cache: fix value accounting after edition and re-delivery
OpValueCache counted every re-delivery of an identical value as a new
source, but the proxy client is a single logical source that re-sends
values on push refresh or listen restart: the refcount grew unbounded
and expirations were never propagated. Add a countSources flag to
OpValueCache, OpCache and SearchCache; the proxy client disables it.

When a value is edited (same id, higher seq), only the reporting source
is known to hold the new version: replace it in place, reset the
refcount to 1 and emit a single add. Ignore older versions from lagging
sources on add, and ignore their expiration so they cannot release a
newer version still held by others.

ValueCache emits an edited value as an add instead of silently
refreshing its old copy, and applies a grace period of one type
expiration while the listen is synced so that a single lost refresh
notification does not drop a value the remote node still holds.

SearchCache::get only answers from the cache once the listen is synced,
so a get never returns a partial view that the network would complete.

Add the single-source OpCacheTester cases enabled by countSources.
2026-09-04 15:18:13 -04:00
Adrien Beraud c81c1f70c8 tests: add listen cache tests for OpValueCache, ValueCache, DhtRunner and proxy
OpCacheTester and ValueCacheTester exercise the listen caches in
isolation: add/edit/expire flows, per-source refcounting, stale
versions, sync status, SearchCache::get and node drop.

DhtRunnerTester and DhtProxyTester gain end-to-end listen tests for
value edition, duplicate puts, multi-source and partial expiration,
get after a synced listen, and the push refresh/expire paths.

The proxy fixture joins the proxy DhtRunner before destroying the
DhtProxyServer so no DHT callback can reach a destroyed server, and
push notifications are never awaited while holding the mutex taken by
listen callbacks: the result is produced on the DHT thread.

Register the new executables in CMake and Meson.
2026-09-04 15:17:34 -04:00
Adrien BeraudandCopilot 3a383ac873 http: close connections on the io_context thread
asio sockets are not thread-safe. http::Connection::close() used to close
the socket synchronously on the calling thread. Request::cancel() and
Request::~Request() (via terminate()) are called from arbitrary threads
(e.g. DhtProxyClient::stop(), cancelAllListeners(), cancelListen()),
so the close raced with in-flight operations on the io_context thread.

In particular, asio::async_connect's range_connect_op closes and
re-opens the socket when trying the next endpoint (common on mobile
where the IPv6 endpoint fails first). A concurrent close() from another
thread corrupts the epoll reactor's per-descriptor state, leading to
crashes in epoll_reactor::deregister_descriptor / register_descriptor
called from range_connect_op::process.

Dispatch the close to the io_context thread when not already on it.
Also keep the connection alive during the TLS handshake, like every
other asynchronous operation, so that the destructor can never run
while an operation is pending.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-04 14:13:20 -04:00
Adrien Beraud 3149fe60c4 proxy: increase OP_MARGIN to 8 hours 2026-09-04 11:04:42 -04:00
Adrien Beraud d5956e0778 proxy: use system_clock for subscriber expiration timeouts
Track subscribe/permanent-put expiration with std::chrono::system_clock
(wall clock) instead of steady_clock in both the proxy client and server,
so persisted expirations survive restarts and map directly to epoch time.

- DhtProxyServer::clock is now system_clock; expireTimer/expireNotifyTimer
  use asio::system_timer.
- DhtProxyClient::refreshSubscriberTimer uses asio::system_timer and
  system_clock::now() for subscriber refresh.
2026-09-04 11:04:42 -04:00
Adrien Beraud 8baf53d078 utils: fix clock conversion overflow with mismatched durations
to_system_time computed its saturation bounds by subtracting a
steady_clock duration from system_clock::time_point::max(). When the
two clocks have different periods (microseconds for system_clock and
nanoseconds for steady_clock on libc++), the common type promotes the
bound to nanoseconds and overflows, so every future time point was
converted to system_clock::time_point::max().

Cast the delta to the target duration before comparing against the
bounds, and saturate from_system_time before the reverse cast for
symmetry.
2026-09-01 22:56:16 -04:00
Adrien Beraud 82ab21dc3f crypto: support explicit EC curve generation 2026-09-01 21:34:36 -04:00
Adrien Beraud d676aef027 http: support legacy X509 lookup signature v4.3.1 2026-08-06 14:05:48 -04:00
Adrien Beraud a2d9fdf334 Recompute runner wakeup after import schedules value expirations. 2026-08-06 13:59:48 -04:00
Adrien Beraud 0cec4135c0 update to 4.3.0 v4.3.0 2026-08-05 16:35:01 -04:00
Adrien Beraud 6f76686a3e ci: update to Debian 13 2026-08-05 16:19:46 -04:00
Adrien Beraud c211b3c376 dht: persist values with system time 2026-08-05 16:14:15 -04:00
Adrien Beraud 0179b6ecc7 test: cover system time state persistence 2026-08-05 16:14:15 -04:00
Adrien Beraud 44e1ad839e net: harden fragmented reply completion 2026-08-05 14:29:03 -04:00
Adrien Beraud dddd97daff tests: cover partial reply hardening 2026-08-05 14:29:03 -04:00