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::{Map, Value, json};
9use thiserror::Error;
10use tokio::sync::mpsc;
11use validator::Validate;
12use wscall_protocol::{
13 EncryptionKind, ErrorPayload, FileAttachment, FrameCodec, PacketEnvelope, ProtocolError,
14};
15
16use crate::validation;
17
18pub(crate) enum ServerOutbound {
19 PreEncoded(Bytes),
24 Packet(PacketEnvelope),
27 Pong(Vec<u8>),
28 Close,
29}
30
31pub(crate) struct ClientEntry {
37 pub sender: mpsc::Sender<ServerOutbound>,
38}
39
40pub(crate) struct ServerState {
42 pub clients: DashMap<String, ClientEntry>,
43 pub next_event_id: AtomicU64,
45}
46
47impl ServerState {
48 pub fn new() -> Self {
49 Self {
50 clients: DashMap::new(),
51 next_event_id: AtomicU64::new(0),
52 }
53 }
54}
55
56impl Default for ServerState {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62#[derive(Clone)]
67pub struct ServerHandle {
68 pub(crate) state: Arc<ServerState>,
69 pub(crate) codec: FrameCodec,
70 pub(crate) default_encryption: EncryptionKind,
71 pub(crate) is_ecdh: bool,
73}
74
75#[derive(Clone)]
77pub struct ServerConnectionContext {
78 pub(crate) connection_id: String,
79 pub(crate) peer_addr: Option<SocketAddr>,
80 pub(crate) server: ServerHandle,
81}
82
83impl ServerConnectionContext {
84 pub fn connection_id(&self) -> &str {
86 &self.connection_id
87 }
88
89 pub fn peer_addr(&self) -> Option<SocketAddr> {
91 self.peer_addr
92 }
93
94 pub fn peer_ip(&self) -> Option<String> {
96 self.peer_addr.map(|addr| addr.ip().to_string())
97 }
98
99 pub fn server(&self) -> &ServerHandle {
101 &self.server
102 }
103}
104
105#[derive(Clone)]
107pub struct ServerDisconnectContext {
108 pub(crate) connection_id: String,
109 pub(crate) peer_addr: Option<SocketAddr>,
110 pub(crate) reason: String,
111 pub(crate) server: ServerHandle,
112}
113
114impl ServerDisconnectContext {
115 pub fn connection_id(&self) -> &str {
117 &self.connection_id
118 }
119
120 pub fn peer_addr(&self) -> Option<SocketAddr> {
122 self.peer_addr
123 }
124
125 pub fn peer_ip(&self) -> Option<String> {
127 self.peer_addr.map(|addr| addr.ip().to_string())
128 }
129
130 pub fn reason(&self) -> &str {
132 &self.reason
133 }
134
135 pub fn server(&self) -> &ServerHandle {
137 &self.server
138 }
139}
140
141#[derive(Clone)]
143pub struct ApiContext {
144 pub(crate) connection_id: String,
145 pub(crate) peer_addr: Option<SocketAddr>,
146 pub(crate) request_id: u64,
147 pub(crate) route: String,
148 pub(crate) params: Value,
149 pub(crate) attachments: Vec<FileAttachment>,
150 pub(crate) metadata: Value,
151 pub(crate) server: ServerHandle,
152}
153
154pub trait ValidateParams {
156 fn validate(&self) -> Result<(), ApiError>;
157}
158
159impl ApiContext {
160 pub fn connection_id(&self) -> &str {
162 &self.connection_id
163 }
164
165 pub fn peer_addr(&self) -> Option<SocketAddr> {
167 self.peer_addr
168 }
169
170 pub fn peer_ip(&self) -> Option<String> {
172 self.peer_addr.map(|addr| addr.ip().to_string())
173 }
174
175 pub fn request_id(&self) -> u64 {
177 self.request_id
178 }
179
180 pub fn route(&self) -> &str {
182 &self.route
183 }
184
185 pub fn params(&self) -> &Value {
187 &self.params
188 }
189
190 pub fn param(&self, key: &str) -> Option<&Value> {
192 self.params.as_object()?.get(key)
193 }
194
195 pub fn require_param(&self, key: &str) -> Result<&Value, ApiError> {
197 self.param(key)
198 .ok_or_else(|| ApiError::bad_request(format!("missing required param: {key}")))
199 }
200
201 pub fn bind<T>(&self) -> Result<T, ApiError>
203 where
204 T: DeserializeOwned,
205 {
206 serde_json::from_value(self.params.clone())
207 .map_err(|source| ApiError::bad_request(format!("invalid params: {source}")))
208 }
209
210 pub(crate) fn bind_take<T>(&mut self) -> Result<T, ApiError>
218 where
219 T: DeserializeOwned,
220 {
221 let params = std::mem::take(&mut self.params);
222 serde_json::from_value(params)
223 .map_err(|source| ApiError::bad_request(format!("invalid params: {source}")))
224 }
225
226 pub fn bind_and_validate<T>(&self) -> Result<T, ApiError>
228 where
229 T: DeserializeOwned + ValidateParams,
230 {
231 let params: T = self.bind()?;
232 params.validate()?;
233 Ok(params)
234 }
235
236 pub fn bind_validated<T>(&self) -> Result<T, ApiError>
238 where
239 T: DeserializeOwned + Validate,
240 {
241 let params: T = self.bind()?;
242 params.validate().map_err(|source| {
243 ApiError::bad_request("params validation failed").with_details(json!({
244 "validation_errors": validation::errors_to_details(&source),
245 }))
246 })?;
247 Ok(params)
248 }
249
250 pub fn attachments(&self) -> &[FileAttachment] {
252 &self.attachments
253 }
254
255 pub fn metadata(&self) -> &Value {
257 &self.metadata
258 }
259
260 pub fn server(&self) -> &ServerHandle {
262 &self.server
263 }
264
265 pub fn attachment_summaries(&self) -> Vec<Value> {
267 self.attachments
268 .iter()
269 .map(|attachment| {
270 json!({
271 "id": attachment.id,
272 "name": attachment.name,
273 "content_type": attachment.content_type,
274 "size": attachment.size(),
275 })
276 })
277 .collect()
278 }
279}
280
281#[derive(Clone)]
283pub struct EventContext {
284 pub(crate) connection_id: String,
285 pub(crate) peer_addr: Option<SocketAddr>,
286 pub(crate) event_id: u64,
287 pub(crate) name: String,
288 pub(crate) data: Map<String, Value>,
289 pub(crate) attachments: Vec<FileAttachment>,
290 pub(crate) metadata: Value,
291 pub(crate) server: ServerHandle,
292}
293
294impl EventContext {
295 pub fn connection_id(&self) -> &str {
297 &self.connection_id
298 }
299
300 pub fn peer_addr(&self) -> Option<SocketAddr> {
302 self.peer_addr
303 }
304
305 pub fn peer_ip(&self) -> Option<String> {
307 self.peer_addr.map(|addr| addr.ip().to_string())
308 }
309
310 pub fn event_id(&self) -> u64 {
312 self.event_id
313 }
314
315 pub fn name(&self) -> &str {
317 &self.name
318 }
319
320 pub fn data(&self) -> &Map<String, Value> {
322 &self.data
323 }
324
325 pub fn attachments(&self) -> &[FileAttachment] {
327 &self.attachments
328 }
329
330 pub fn metadata(&self) -> &Value {
332 &self.metadata
333 }
334
335 pub fn server(&self) -> &ServerHandle {
337 &self.server
338 }
339}
340
341#[derive(Clone)]
343pub struct ExceptionContext {
344 pub connection_id: String,
345 pub request_id: Option<u64>,
346 pub target: String,
347 pub message_kind: &'static str,
348 pub error: ApiError,
349}
350
351#[derive(Debug, Clone, Error)]
353#[error("{code}: {message}")]
354pub struct ApiError {
355 pub code: String,
356 pub message: String,
357 pub status: u16,
358 pub details: Option<Value>,
359}
360
361impl ApiError {
362 pub fn bad_request(message: impl Into<String>) -> Self {
364 Self::new("bad_request", message, 400)
365 }
366
367 pub fn not_found(message: impl Into<String>) -> Self {
369 Self::new("not_found", message, 404)
370 }
371
372 pub fn internal(message: impl Into<String>) -> Self {
374 Self::new("internal_error", message, 500)
375 }
376
377 pub fn new(code: impl Into<String>, message: impl Into<String>, status: u16) -> Self {
379 Self {
380 code: code.into(),
381 message: message.into(),
382 status,
383 details: None,
384 }
385 }
386
387 pub fn with_details(mut self, details: Value) -> Self {
389 self.details = Some(details);
390 self
391 }
392
393 pub fn into_payload(self) -> ErrorPayload {
395 ErrorPayload {
396 code: self.code,
397 message: self.message,
398 status: self.status,
399 details: self.details,
400 }
401 }
402}
403
404#[derive(Debug, Error)]
406pub enum ServerError {
407 #[error("io error: {0}")]
408 Io(#[from] std::io::Error),
409 #[error("websocket error: {0}")]
410 WebSocket(#[from] tokio_tungstenite::tungstenite::Error),
411 #[error("protocol error: {0}")]
412 Protocol(#[from] ProtocolError),
413 #[error("api error: {0:?}")]
414 Api(#[from] ApiError),
415 #[error("connection idle timeout: {0}")]
416 IdleTimeout(String),
417 #[error("outbound queue is full for connection {0}")]
418 OutboundQueueFull(String),
419}