1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! WebSocket route registration API for plugins.
//!
//! [`WsRouter`] is a build-time API that plugins use to register axum route
//! fragments containing WebSocket upgrade handlers. [`AppPlugin`](crate::AppPlugin)
//! merges all registered fragments in `ready()` before starting the server.
//!
//! WebSocket routes go through the same middleware stack (CORS, tracing,
//! request ID, auth) as regular HTTP routes because they are merged into the
//! main axum router before middleware is applied. This means
//! [`AuthProvider`](crate::AuthProvider) validates WebSocket upgrade requests
//! just like REST requests.
//!
//! Authentication is handled by the existing
//! [`HttpRouter::set_auth`](crate::HttpRouter::set_auth) -- `WsRouter` only
//! provides route registration.
//!
//! # Example
//!
//! ```no_run
//! use polaris_system::plugin::{Plugin, PluginId, Version};
//! use polaris_system::server::Server;
//! use polaris_app::{AppPlugin, WsRouter};
//! use axum::{Router, routing::get, extract::ws::{WebSocketUpgrade, WebSocket}};
//! use axum::response::IntoResponse;
//!
//! struct EchoWsPlugin;
//!
//! async fn ws_handler(ws: WebSocketUpgrade) -> impl IntoResponse {
//! ws.on_upgrade(handle_socket)
//! }
//!
//! async fn handle_socket(mut socket: WebSocket) {
//! // echo logic
//! }
//!
//! impl Plugin for EchoWsPlugin {
//! const ID: &'static str = "myapp::echo_ws";
//! const VERSION: Version = Version::new(0, 1, 0);
//!
//! fn build(&self, server: &mut Server) {
//! let router = Router::new()
//! .route("/ws/echo", get(ws_handler));
//! server.api::<WsRouter>()
//! .expect("AppPlugin with `ws` feature must be added first")
//! .add_routes(router);
//! }
//!
//! fn dependencies(&self) -> Vec<PluginId> {
//! vec![PluginId::of::<AppPlugin>()]
//! }
//! }
//! ```
use RwLock;
use API;
/// Build-time API for registering WebSocket routes.
///
/// Plugins call [`add_routes`](WsRouter::add_routes) during their `build()`
/// phase to contribute route fragments that contain WebSocket upgrade handlers.
/// [`AppPlugin`](crate::AppPlugin) merges all fragments in `ready()` alongside
/// HTTP route fragments, before applying the middleware stack.
///
/// Uses interior mutability (`RwLock`) so `server.api::<WsRouter>()` returns
/// `&WsRouter` while still allowing registration.
///
/// Authentication for WebSocket upgrade requests is handled by the existing
/// [`HttpRouter::set_auth`](crate::HttpRouter::set_auth) mechanism -- there is
/// no separate auth on `WsRouter`.