1use std::net::SocketAddr;
2use std::sync::Arc;
3use std::sync::atomic::AtomicU64;
4
5use bytes::Bytes;
6use dashmap::DashMap;
7use serde::de::DeserializeOwned;
8use serde_json::{Value, json};
9use thiserror::Error;
10use tokio::sync::mpsc;
11use validator::Validate;
12use wscall_protocol::{EncryptionKind, ErrorPayload, FileAttachment, FrameCodec, ProtocolError};
13
14use crate::validation;
15
16pub(crate) enum ServerOutbound {
17 PreEncoded(Bytes),
22 Pong(Vec<u8>),
23 Close,
24}
25
26pub(crate) struct ServerState {
28 pub clients: DashMap<String, mpsc::Sender<ServerOutbound>>,
29 pub next_event_id: AtomicU64,
31}
32
33impl ServerState {
34 pub fn new() -> Self {
35 Self {
36 clients: DashMap::new(),
37 next_event_id: AtomicU64::new(0),
38 }
39 }
40}
41
42impl Default for ServerState {
43 fn default() -> Self {
44 Self::new()
45 }
46}
47
48#[derive(Clone)]
53pub struct ServerHandle {
54 pub(crate) state: Arc<ServerState>,
55 pub(crate) codec: FrameCodec,
56 pub(crate) default_encryption: EncryptionKind,
57}
58
59#[derive(Clone)]
61pub struct ServerConnectionContext {
62 pub(crate) connection_id: String,
63 pub(crate) peer_addr: Option<SocketAddr>,
64 pub(crate) server: ServerHandle,
65}
66
67impl ServerConnectionContext {
68 pub fn connection_id(&self) -> &str {
70 &self.connection_id
71 }
72
73 pub fn peer_addr(&self) -> Option<SocketAddr> {
75 self.peer_addr
76 }
77
78 pub fn peer_ip(&self) -> Option<String> {
80 self.peer_addr.map(|addr| addr.ip().to_string())
81 }
82
83 pub fn server(&self) -> &ServerHandle {
85 &self.server
86 }
87}
88
89#[derive(Clone)]
91pub struct ServerDisconnectContext {
92 pub(crate) connection_id: String,
93 pub(crate) peer_addr: Option<SocketAddr>,
94 pub(crate) reason: String,
95 pub(crate) server: ServerHandle,
96}
97
98impl ServerDisconnectContext {
99 pub fn connection_id(&self) -> &str {
101 &self.connection_id
102 }
103
104 pub fn peer_addr(&self) -> Option<SocketAddr> {
106 self.peer_addr
107 }
108
109 pub fn peer_ip(&self) -> Option<String> {
111 self.peer_addr.map(|addr| addr.ip().to_string())
112 }
113
114 pub fn reason(&self) -> &str {
116 &self.reason
117 }
118
119 pub fn server(&self) -> &ServerHandle {
121 &self.server
122 }
123}
124
125#[derive(Clone)]
127pub struct ApiContext {
128 pub(crate) connection_id: String,
129 pub(crate) peer_addr: Option<SocketAddr>,
130 pub(crate) request_id: u64,
131 pub(crate) route: String,
132 pub(crate) params: Value,
133 pub(crate) attachments: Vec<FileAttachment>,
134 pub(crate) metadata: Value,
135 pub(crate) server: ServerHandle,
136}
137
138pub trait ValidateParams {
140 fn validate(&self) -> Result<(), ApiError>;
141}
142
143impl ApiContext {
144 pub fn connection_id(&self) -> &str {
146 &self.connection_id
147 }
148
149 pub fn peer_addr(&self) -> Option<SocketAddr> {
151 self.peer_addr
152 }
153
154 pub fn peer_ip(&self) -> Option<String> {
156 self.peer_addr.map(|addr| addr.ip().to_string())
157 }
158
159 pub fn request_id(&self) -> u64 {
161 self.request_id
162 }
163
164 pub fn route(&self) -> &str {
166 &self.route
167 }
168
169 pub fn params(&self) -> &Value {
171 &self.params
172 }
173
174 pub fn param(&self, key: &str) -> Option<&Value> {
176 self.params.as_object()?.get(key)
177 }
178
179 pub fn require_param(&self, key: &str) -> Result<&Value, ApiError> {
181 self.param(key)
182 .ok_or_else(|| ApiError::bad_request(format!("missing required param: {key}")))
183 }
184
185 pub fn bind<T>(&self) -> Result<T, ApiError>
187 where
188 T: DeserializeOwned,
189 {
190 serde_json::from_value(self.params.clone())
191 .map_err(|source| ApiError::bad_request(format!("invalid params: {source}")))
192 }
193
194 pub fn bind_and_validate<T>(&self) -> Result<T, ApiError>
196 where
197 T: DeserializeOwned + ValidateParams,
198 {
199 let params: T = self.bind()?;
200 params.validate()?;
201 Ok(params)
202 }
203
204 pub fn bind_validated<T>(&self) -> Result<T, ApiError>
206 where
207 T: DeserializeOwned + Validate,
208 {
209 let params: T = self.bind()?;
210 params.validate().map_err(|source| {
211 ApiError::bad_request("params validation failed").with_details(json!({
212 "validation_errors": validation::errors_to_details(&source),
213 }))
214 })?;
215 Ok(params)
216 }
217
218 pub fn attachments(&self) -> &[FileAttachment] {
220 &self.attachments
221 }
222
223 pub fn metadata(&self) -> &Value {
225 &self.metadata
226 }
227
228 pub fn server(&self) -> &ServerHandle {
230 &self.server
231 }
232
233 pub fn attachment_summaries(&self) -> Vec<Value> {
235 self.attachments
236 .iter()
237 .map(|attachment| {
238 json!({
239 "id": attachment.id,
240 "name": attachment.name,
241 "content_type": attachment.content_type,
242 "size": attachment.size,
243 })
244 })
245 .collect()
246 }
247}
248
249#[derive(Clone)]
251pub struct EventContext {
252 pub(crate) connection_id: String,
253 pub(crate) peer_addr: Option<SocketAddr>,
254 pub(crate) event_id: u64,
255 pub(crate) name: String,
256 pub(crate) data: Value,
257 pub(crate) attachments: Vec<FileAttachment>,
258 pub(crate) metadata: Value,
259 pub(crate) storage_id: Option<u64>,
260 pub(crate) server: ServerHandle,
261}
262
263impl EventContext {
264 pub fn connection_id(&self) -> &str {
266 &self.connection_id
267 }
268
269 pub fn peer_addr(&self) -> Option<SocketAddr> {
271 self.peer_addr
272 }
273
274 pub fn peer_ip(&self) -> Option<String> {
276 self.peer_addr.map(|addr| addr.ip().to_string())
277 }
278
279 pub fn event_id(&self) -> u64 {
281 self.event_id
282 }
283
284 pub fn name(&self) -> &str {
286 &self.name
287 }
288
289 pub fn data(&self) -> &Value {
291 &self.data
292 }
293
294 pub fn attachments(&self) -> &[FileAttachment] {
296 &self.attachments
297 }
298
299 pub fn metadata(&self) -> &Value {
301 &self.metadata
302 }
303
304 pub fn storage_id(&self) -> Option<u64> {
306 self.storage_id
307 }
308
309 pub fn server(&self) -> &ServerHandle {
311 &self.server
312 }
313}
314
315#[derive(Clone)]
317pub struct ExceptionContext {
318 pub connection_id: String,
319 pub request_id: Option<u64>,
320 pub target: String,
321 pub message_kind: &'static str,
322 pub error: ApiError,
323}
324
325#[derive(Debug, Clone, Error)]
327#[error("{code}: {message}")]
328pub struct ApiError {
329 pub code: String,
330 pub message: String,
331 pub status: u16,
332 pub details: Option<Value>,
333}
334
335impl ApiError {
336 pub fn bad_request(message: impl Into<String>) -> Self {
338 Self::new("bad_request", message, 400)
339 }
340
341 pub fn not_found(message: impl Into<String>) -> Self {
343 Self::new("not_found", message, 404)
344 }
345
346 pub fn internal(message: impl Into<String>) -> Self {
348 Self::new("internal_error", message, 500)
349 }
350
351 pub fn new(code: impl Into<String>, message: impl Into<String>, status: u16) -> Self {
353 Self {
354 code: code.into(),
355 message: message.into(),
356 status,
357 details: None,
358 }
359 }
360
361 pub fn with_details(mut self, details: Value) -> Self {
363 self.details = Some(details);
364 self
365 }
366
367 pub fn into_payload(self) -> ErrorPayload {
369 ErrorPayload {
370 code: self.code,
371 message: self.message,
372 status: self.status,
373 details: self.details,
374 }
375 }
376}
377
378#[derive(Debug, Error)]
380pub enum ServerError {
381 #[error("io error: {0}")]
382 Io(#[from] std::io::Error),
383 #[error("websocket error: {0}")]
384 WebSocket(#[from] tokio_tungstenite::tungstenite::Error),
385 #[error("protocol error: {0}")]
386 Protocol(#[from] ProtocolError),
387 #[error("api error: {0:?}")]
388 Api(#[from] ApiError),
389 #[error("connection idle timeout: {0}")]
390 IdleTimeout(String),
391 #[error("outbound queue is full for connection {0}")]
392 OutboundQueueFull(String),
393}