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 fn bind_and_validate<T>(&self) -> Result<T, ApiError>
212 where
213 T: DeserializeOwned + ValidateParams,
214 {
215 let params: T = self.bind()?;
216 params.validate()?;
217 Ok(params)
218 }
219
220 pub fn bind_validated<T>(&self) -> Result<T, ApiError>
222 where
223 T: DeserializeOwned + Validate,
224 {
225 let params: T = self.bind()?;
226 params.validate().map_err(|source| {
227 ApiError::bad_request("params validation failed").with_details(json!({
228 "validation_errors": validation::errors_to_details(&source),
229 }))
230 })?;
231 Ok(params)
232 }
233
234 pub fn attachments(&self) -> &[FileAttachment] {
236 &self.attachments
237 }
238
239 pub fn metadata(&self) -> &Value {
241 &self.metadata
242 }
243
244 pub fn server(&self) -> &ServerHandle {
246 &self.server
247 }
248
249 pub fn attachment_summaries(&self) -> Vec<Value> {
251 self.attachments
252 .iter()
253 .map(|attachment| {
254 json!({
255 "id": attachment.id,
256 "name": attachment.name,
257 "content_type": attachment.content_type,
258 "size": attachment.size(),
259 })
260 })
261 .collect()
262 }
263}
264
265#[derive(Clone)]
267pub struct EventContext {
268 pub(crate) connection_id: String,
269 pub(crate) peer_addr: Option<SocketAddr>,
270 pub(crate) event_id: u64,
271 pub(crate) name: String,
272 pub(crate) data: Map<String, Value>,
273 pub(crate) attachments: Vec<FileAttachment>,
274 pub(crate) metadata: Value,
275 pub(crate) server: ServerHandle,
276}
277
278impl EventContext {
279 pub fn connection_id(&self) -> &str {
281 &self.connection_id
282 }
283
284 pub fn peer_addr(&self) -> Option<SocketAddr> {
286 self.peer_addr
287 }
288
289 pub fn peer_ip(&self) -> Option<String> {
291 self.peer_addr.map(|addr| addr.ip().to_string())
292 }
293
294 pub fn event_id(&self) -> u64 {
296 self.event_id
297 }
298
299 pub fn name(&self) -> &str {
301 &self.name
302 }
303
304 pub fn data(&self) -> &Map<String, Value> {
306 &self.data
307 }
308
309 pub fn attachments(&self) -> &[FileAttachment] {
311 &self.attachments
312 }
313
314 pub fn metadata(&self) -> &Value {
316 &self.metadata
317 }
318
319 pub fn server(&self) -> &ServerHandle {
321 &self.server
322 }
323}
324
325#[derive(Clone)]
327pub struct ExceptionContext {
328 pub connection_id: String,
329 pub request_id: Option<u64>,
330 pub target: String,
331 pub message_kind: &'static str,
332 pub error: ApiError,
333}
334
335#[derive(Debug, Clone, Error)]
337#[error("{code}: {message}")]
338pub struct ApiError {
339 pub code: String,
340 pub message: String,
341 pub status: u16,
342 pub details: Option<Value>,
343}
344
345impl ApiError {
346 pub fn bad_request(message: impl Into<String>) -> Self {
348 Self::new("bad_request", message, 400)
349 }
350
351 pub fn not_found(message: impl Into<String>) -> Self {
353 Self::new("not_found", message, 404)
354 }
355
356 pub fn internal(message: impl Into<String>) -> Self {
358 Self::new("internal_error", message, 500)
359 }
360
361 pub fn new(code: impl Into<String>, message: impl Into<String>, status: u16) -> Self {
363 Self {
364 code: code.into(),
365 message: message.into(),
366 status,
367 details: None,
368 }
369 }
370
371 pub fn with_details(mut self, details: Value) -> Self {
373 self.details = Some(details);
374 self
375 }
376
377 pub fn into_payload(self) -> ErrorPayload {
379 ErrorPayload {
380 code: self.code,
381 message: self.message,
382 status: self.status,
383 details: self.details,
384 }
385 }
386}
387
388#[derive(Debug, Error)]
390pub enum ServerError {
391 #[error("io error: {0}")]
392 Io(#[from] std::io::Error),
393 #[error("websocket error: {0}")]
394 WebSocket(#[from] tokio_tungstenite::tungstenite::Error),
395 #[error("protocol error: {0}")]
396 Protocol(#[from] ProtocolError),
397 #[error("api error: {0:?}")]
398 Api(#[from] ApiError),
399 #[error("connection idle timeout: {0}")]
400 IdleTimeout(String),
401 #[error("outbound queue is full for connection {0}")]
402 OutboundQueueFull(String),
403}