fix(nestsClient): token-parse hardening, mint timeout, IPv6 [], broadcaster-bail signal
- NestsTokenResponse.parse catch list now includes IllegalStateException so a malformed escape from a misbehaving auth server surfaces as NestsException instead of crashing the listener. - OkHttpNestsClient gains a configurable callTimeoutMs (default 90 s) enforcing an upper bound on the entire mintToken round-trip including retries. Without it a stalled server suspends the reconnect orchestrator indefinitely (the orchestrator's openOnce step parks on mintToken). 90 s leaves headroom over the worst-case 63 s 429 retry chain documented in MAX_RATE_LIMIT_RETRIES. - parseEndpoint tightens IPv6 bracket check from `closeBracket > 0` to `> 1`, rejecting the empty `[]` literal. - NestBroadcaster + NestMoqLiteBroadcaster gain an `onTerminalFailure` callback that fires once when the consecutive-send-error guard bails. MoqLiteNestsSpeaker wires this to flip the speaker state to Failed, giving ReconnectingNestsSpeaker the signal it needs to recycle the session — without this hook the broadcaster bailed silently and the outward speaker state stayed on Broadcasting forever. Adds regression test: - onTerminalFailure_fires_once_after_consecutive_send_failures 225 tests pass, 0 failures. Android target compiles clean.
This commit is contained in:
+30
-1
@@ -92,7 +92,20 @@ class MoqLiteNestsSpeaker internal constructor(
|
||||
publisher = publisher,
|
||||
scope = scope,
|
||||
framesPerGroup = framesPerGroup,
|
||||
).also { it.start() }
|
||||
).also {
|
||||
it.start(
|
||||
onTerminalFailure = {
|
||||
// Broadcaster bailed after sustained
|
||||
// publisher.send failures. Flip to
|
||||
// Failed so the reconnect orchestrator
|
||||
// sees a terminal state and recycles
|
||||
// the session — without this signal the
|
||||
// outward state stays on Broadcasting
|
||||
// and the room is silently mute.
|
||||
onBroadcastTerminalFailure()
|
||||
},
|
||||
)
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
runCatching { publisher.close() }
|
||||
throw t
|
||||
@@ -129,6 +142,22 @@ class MoqLiteNestsSpeaker internal constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the broadcaster's `onTerminalFailure` callback (off
|
||||
* the speaker's coroutine). Transitions the speaker to `Failed` so
|
||||
* the reconnect orchestrator (`ReconnectingNestsSpeaker`) observes
|
||||
* a terminal state and recycles the session. No-op if the speaker
|
||||
* is already in a terminal state.
|
||||
*/
|
||||
private fun onBroadcastTerminalFailure() {
|
||||
val current = mutableState.value
|
||||
if (current is NestsSpeakerState.Failed || current is NestsSpeakerState.Closed) return
|
||||
mutableState.value =
|
||||
NestsSpeakerState.Failed(
|
||||
reason = "broadcast pipeline gave up — likely transport loss",
|
||||
)
|
||||
}
|
||||
|
||||
internal fun reportMuteState(muted: Boolean) {
|
||||
val current = mutableState.value
|
||||
if (current is NestsSpeakerState.Broadcasting) {
|
||||
|
||||
Reference in New Issue
Block a user