feat(websocket): data-channel + op/register wiring (review 006 Unit 2+3)
The WS path wires the alkcall 0.3 per-session mechanisms — the OQ-05 deferred half (review-003 WS-20/21/22/25/26, the decisions WS-24/WS-25 resolved upstream): - install_channel_zero reworked per the ADR-047 §4 amendment #2 shape: fork the deployment's base registry, register the generic channel ops (channel/close, channel/control, channel/resources/subscribe — WS-21), the deployment's openable ALPNs (ChannelCore::register_openable, WS-22), the bootstrap discovery set closed over the fork (install_bootstrap_discovery, F-06), and op/register (WS-25; the collision set is the fork per review-005 G-03), then dispatch over the fork. The session's ChannelsPolicy rides the hook (one policy instance across open wrappers and the demux teardown path). - OpenableAlpn { spec, open_handler } + HttpAdapter::with_ws_openable_alpns, threaded RouterState -> SessionState -> hook, with the OpenableAlpns request-extension fallback (mirroring ChannelsPolicy/WsTimeouts). - WsSessions retains the channel-0 Arc<CallConnection> (WS-26) with a self-removing guard (ConnectionGuard); live_connections() is the deployment-visible surface. - UP-01: ALREADY_EXISTS maps to 409 Conflict in the gateway error map. - from_wss import excludes the protocol-session ops (bootstrap set + channel lifecycle ops): the fork serves them per session, and proxying session-scoped machinery (e.g. channel/close across sessions) would be nonsense. Discovery runs first, the filter is the listing minus those names. - adapter_install_channel_zero cfg matches its caller (WS-27); it inherits the reworked hook (session ops now served in the from_wss test-server producer too). Gates (Unit 3, tests/ws_upgrade_session.rs; the WS-23 e2e set): open -> channel_id -> discoverable in services/list -> chunks both ways -> handler sees bytes; channel/close resolves + ledger decrement; cap denial (channel:-prefixed); mid-open disconnect teardown; TooLarge demux resync through the WS path (16 MiB + 1 skip consumed); op/register announce + overlay-collision + serving-registry-collision ALREADY_EXISTS through the WS path. call_and_await now filters by request id and tolerates data-channel chunks (a prior Sub's trailing call.completed may interleave). Verification: cargo test 454 (default) / 582 (all-features), clippy both sides -D warnings clean, fmt clean, doc clean.
This commit is contained in:
@@ -48,6 +48,11 @@ pub(crate) struct RouterState {
|
||||
pub(crate) ws_session_slots: Arc<tokio::sync::Semaphore>,
|
||||
/// Idle-read timeout for the WS pumps (WS-01); `None` disables.
|
||||
pub(crate) ws_idle_timeout: Option<std::time::Duration>,
|
||||
/// The openable-ALPN set for WS sessions (WS-22): the per-ALPN
|
||||
/// open-op specs + handlers registered on each session's fork.
|
||||
/// `None` (the default) declares no openables — a WS session then
|
||||
/// carries no data-channel open ops (the pre-Unit-2 shape).
|
||||
pub(crate) ws_openable_alpns: Option<Arc<[crate::websocket::OpenableAlpn]>>,
|
||||
}
|
||||
|
||||
impl axum::extract::FromRef<RouterState> for crate::websocket::SessionState {
|
||||
@@ -57,6 +62,7 @@ impl axum::extract::FromRef<RouterState> for crate::websocket::SessionState {
|
||||
Arc::clone(&state.ws_sessions),
|
||||
Arc::clone(&state.ws_session_slots),
|
||||
state.ws_idle_timeout,
|
||||
state.ws_openable_alpns.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -96,6 +102,7 @@ mod tests {
|
||||
crate::websocket::DEFAULT_WS_MAX_SESSIONS,
|
||||
)),
|
||||
ws_idle_timeout: Some(crate::websocket::DEFAULT_WS_IDLE_TIMEOUT),
|
||||
ws_openable_alpns: None,
|
||||
};
|
||||
let extracted: DecoyConfig = axum::extract::FromRef::from_ref(&state);
|
||||
assert!(matches!(extracted, DecoyConfig::Redirect { .. }));
|
||||
|
||||
Reference in New Issue
Block a user