XMPP: MUC leave() tijdens lopende join laat join-future hangen tot timeout #1427

Closed
opened 2026-08-09 18:34:21 +00:00 by brenno · 0 comments
Owner

Bevinding

lib/xmpp/xmpp_muc.dart — als leave() wordt aangeroepen terwijl een join nog loopt (_joining != null, _joined == false), wordt de join-completer nooit voltooid. De join-future hangt tot de join-timeout (20 seconden) of een session-drop.

Wat er gebeurt

leave() (regels 266–276) zet _left = true, stuurt een unavailable-presence, en roept _teardown() aan. _teardown() (regels 278–285) annuleert de subscription en sluit de roster-stream, maar completeert de join-completer niet.

De _joining-completer (regel 145) wordt alleen voltooid in _finishJoin (regel 252), die alleen wordt aangeroepen bij:

  • Een self-presence (join geslaagd)
  • Een error-presence (join gefaald)
  • De join-timer (timeout)
  • _onSessionDropped (session gesloten)

Maar _teardown() annuleert de subscription (regel 282) — waardoor _onSessionDropped niet meer vuurt — en sluit de roster (regel 284). De join-timer loopt nog wel, maar die wacht de volle 20 seconden voordat hij _finishJoin aanroept met timeout.

Repro-scenario

  1. muc.join() wordt aangeroepen — _joining is gezet, join-future is pending
  2. De gebruiker annuleert de join (bijv. navigeert weg uit de dialoog)
  3. leave() wordt aangeroepen
  4. _teardown() annuleert de subscription en sluit de roster
  5. De join-future hangt 20 seconden tot de timer fires
  6. Na 20 seconden voltooid _finishJoin met MucJoinFailure.timeout

In die 20 seconden kan de aanroeper die op join() wacht niets anders doen — de future is pending.

Trust boundary

Interne logica — geen externe aanvaller. Een gebruiker die annuleert tijdens de join.

Impact

  • De join-future hangt 20 seconden na een leave() tijdens join.
  • De aanroeper die op join() wacht kan niet doorgaan tot de future voltooid is.
  • In een UI-flow betekent dit dat de gebruiker 20 seconden moet wachten na annuleren voordat de dialoog sluit.

Oplossingsrichting

Completeer de join-completer in _teardown() als de join nog loopt. Voeg aan _teardown() (of aan leave()) toe:

if (_joining != null && !_joining!.isCompleted) {
  _joining!.complete(const MucJoinResult.failed(MucJoinFailure.sessionClosed));
  _joining = null;
}

En annuleer de join-timer expliciet in _teardown():

_joinTimer?.cancel();
_joinTimer = null;

Locatie

  • lib/xmpp/xmpp_muc.dart regels 266–276 (leave), 278–285 (_teardown), 145–163 (join — completer), 247–254 (_finishJoin)

Severity

MEDIUM — 20 seconden hang bij annuleren is een merkbare UX-bug.

## Bevinding `lib/xmpp/xmpp_muc.dart` — als `leave()` wordt aangeroepen terwijl een join nog loopt (`_joining != null`, `_joined == false`), wordt de join-completer nooit voltooid. De join-future hangt tot de join-timeout (20 seconden) of een session-drop. ### Wat er gebeurt `leave()` (regels 266–276) zet `_left = true`, stuurt een `unavailable`-presence, en roept `_teardown()` aan. `_teardown()` (regels 278–285) annuleert de subscription en sluit de roster-stream, maar **completeert de join-completer niet**. De `_joining`-completer (regel 145) wordt alleen voltooid in `_finishJoin` (regel 252), die alleen wordt aangeroepen bij: - Een self-presence (join geslaagd) - Een error-presence (join gefaald) - De join-timer (timeout) - `_onSessionDropped` (session gesloten) Maar `_teardown()` annuleert de subscription (regel 282) — waardoor `_onSessionDropped` niet meer vuurt — en sluit de roster (regel 284). De join-timer loopt nog wel, maar die wacht de volle 20 seconden voordat hij `_finishJoin` aanroept met `timeout`. ### Repro-scenario 1. `muc.join()` wordt aangeroepen — `_joining` is gezet, join-future is pending 2. De gebruiker annuleert de join (bijv. navigeert weg uit de dialoog) 3. `leave()` wordt aangeroepen 4. `_teardown()` annuleert de subscription en sluit de roster 5. De join-future hangt 20 seconden tot de timer fires 6. Na 20 seconden voltooid `_finishJoin` met `MucJoinFailure.timeout` In die 20 seconden kan de aanroeper die op `join()` wacht niets anders doen — de future is pending. ### Trust boundary Interne logica — geen externe aanvaller. Een gebruiker die annuleert tijdens de join. ### Impact - De join-future hangt 20 seconden na een `leave()` tijdens join. - De aanroeper die op `join()` wacht kan niet doorgaan tot de future voltooid is. - In een UI-flow betekent dit dat de gebruiker 20 seconden moet wachten na annuleren voordat de dialoog sluit. ### Oplossingsrichting Completeer de join-completer in `_teardown()` als de join nog loopt. Voeg aan `_teardown()` (of aan `leave()`) toe: ```dart if (_joining != null && !_joining!.isCompleted) { _joining!.complete(const MucJoinResult.failed(MucJoinFailure.sessionClosed)); _joining = null; } ``` En annuleer de join-timer expliciet in `_teardown()`: ```dart _joinTimer?.cancel(); _joinTimer = null; ``` ### Locatie - `lib/xmpp/xmpp_muc.dart` regels 266–276 (`leave`), 278–285 (`_teardown`), 145–163 (`join` — completer), 247–254 (`_finishJoin`) ### Severity MEDIUM — 20 seconden hang bij annuleren is een merkbare UX-bug.
brenno 2026-08-09 20:56:36 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
LibreKAT/Ocideck#1427
No description provided.