Skip to main content

wsio_server/
builder.rs

1use std::time::Duration;
2
3use tokio_tungstenite::tungstenite::protocol::WebSocketConfig;
4
5use crate::{
6    WsIoServer,
7    config::WsIoServerConfig,
8    core::packet::{
9        codecs::WsIoPacketCodec,
10        transformers::WsIoPacketTransformer,
11    },
12    runtime::WsIoServerRuntime,
13};
14
15// Structs
16
17/// Builder for configuring and creating a [`WsIoServer`].
18///
19/// Server-level settings are inherited by namespaces created from the server.
20/// Namespace builders may override most settings per namespace.
21#[derive(Debug)]
22#[must_use]
23pub struct WsIoServerBuilder {
24    config: WsIoServerConfig,
25}
26
27impl WsIoServerBuilder {
28    pub(crate) fn new() -> Self {
29        Self {
30            config: WsIoServerConfig {
31                broadcast_concurrency_limit: 512,
32                http_request_upgrade_timeout: Duration::from_secs(3),
33                init_request_handler_timeout: Duration::from_secs(3),
34                init_response_handler_timeout: Duration::from_secs(3),
35                init_response_timeout: Duration::from_secs(5),
36                middleware_execution_timeout: Duration::from_secs(2),
37                on_close_handler_timeout: Duration::from_secs(2),
38                on_connect_handler_timeout: Duration::from_secs(3),
39                packet_codec: WsIoPacketCodec::Msgpack,
40                packet_transformer: WsIoPacketTransformer::default(),
41                request_path: "/ws.io".to_owned(),
42                websocket_config: WebSocketConfig::default()
43                    .max_frame_size(Some(8 * 1024 * 1024))
44                    .max_message_size(Some(16 * 1024 * 1024))
45                    .max_write_buffer_size(2 * 1024 * 1024)
46                    .read_buffer_size(8 * 1024)
47                    .write_buffer_size(8 * 1024),
48            },
49        }
50    }
51
52    // Public methods
53    /// Sets the default maximum number of concurrent broadcast sends.
54    ///
55    /// Namespace builders inherit this value. It is passed to
56    /// `StreamExt::for_each_concurrent`; `0` means unlimited concurrency.
57    pub fn broadcast_concurrency_limit(mut self, broadcast_concurrency_limit: usize) -> Self {
58        self.config.broadcast_concurrency_limit = broadcast_concurrency_limit;
59        self
60    }
61
62    /// Builds a [`WsIoServer`] from this builder's configuration.
63    pub fn build(self) -> WsIoServer {
64        WsIoServer(WsIoServerRuntime::new(self.config))
65    }
66
67    /// Sets the default maximum duration for a matched HTTP request's WebSocket
68    /// upgrade.
69    ///
70    /// The timeout wraps the HTTP adapter's upgrade future. Namespace builders
71    /// inherit this value and may override it.
72    pub fn http_request_upgrade_timeout(mut self, duration: Duration) -> Self {
73        self.config.http_request_upgrade_timeout = duration;
74        self
75    }
76
77    /// Sets the default maximum duration for init-request handlers.
78    ///
79    /// Namespace init-request handlers are registered with
80    /// `WsIoServerNamespaceBuilder::with_init_request`.
81    pub fn init_request_handler_timeout(mut self, duration: Duration) -> Self {
82        self.config.init_request_handler_timeout = duration;
83        self
84    }
85
86    /// Sets the default maximum duration for init-response handlers.
87    ///
88    /// Namespace init-response handlers are registered with
89    /// `WsIoServerNamespaceBuilder::with_init_response`.
90    pub fn init_response_handler_timeout(mut self, duration: Duration) -> Self {
91        self.config.init_response_handler_timeout = duration;
92        self
93    }
94
95    /// Sets the default maximum duration for waiting for a client init response.
96    ///
97    /// This starts after the server sends its init packet. Namespace builders
98    /// inherit this value and may override it.
99    pub fn init_response_timeout(mut self, duration: Duration) -> Self {
100        self.config.init_response_timeout = duration;
101        self
102    }
103
104    /// Sets the default maximum duration for namespace middleware.
105    ///
106    /// Namespace middleware is registered with
107    /// `WsIoServerNamespaceBuilder::with_middleware`.
108    pub fn middleware_execution_timeout(mut self, duration: Duration) -> Self {
109        self.config.middleware_execution_timeout = duration;
110        self
111    }
112
113    /// Sets the default maximum duration for per-connection close handlers.
114    ///
115    /// This applies to handlers registered through
116    /// `WsIoServerConnection::on_close`.
117    pub fn on_close_handler_timeout(mut self, duration: Duration) -> Self {
118        self.config.on_close_handler_timeout = duration;
119        self
120    }
121
122    /// Sets the default maximum duration for namespace on-connect handlers.
123    ///
124    /// Namespace on-connect handlers are registered with
125    /// `WsIoServerNamespaceBuilder::on_connect`.
126    pub fn on_connect_handler_timeout(mut self, duration: Duration) -> Self {
127        self.config.on_connect_handler_timeout = duration;
128        self
129    }
130
131    /// Sets the default packet codec for namespaces.
132    ///
133    /// The codec handles ws.io protocol packets and init payload data. Namespace
134    /// builders inherit this value and may override it.
135    pub fn packet_codec(mut self, packet_codec: WsIoPacketCodec) -> Self {
136        self.config.packet_codec = packet_codec;
137        self
138    }
139
140    /// Sets the default packet transformer for namespaces.
141    ///
142    /// A namespace builder may override it for that namespace only.
143    pub fn packet_transformer(mut self, packet_transformer: WsIoPacketTransformer) -> Self {
144        self.config.packet_transformer = packet_transformer;
145        self
146    }
147
148    /// Sets the HTTP request path handled by the server adapter.
149    ///
150    /// Requests with a different URI path pass through to the wrapped service.
151    /// Client namespace selection is carried separately in the `namespace` query
152    /// parameter.
153    pub fn request_path(mut self, request_path: impl AsRef<str>) -> Self {
154        request_path.as_ref().clone_into(&mut self.config.request_path);
155        self
156    }
157
158    /// Replaces the default Tungstenite WebSocket configuration.
159    ///
160    /// Namespace builders inherit this value. It controls transport limits and
161    /// buffer sizes and derives internal channel capacity from the configured
162    /// max-write/write-buffer ratio.
163    pub fn websocket_config(mut self, websocket_config: WebSocketConfig) -> Self {
164        self.config.websocket_config = websocket_config;
165        self
166    }
167
168    /// Mutates the current default Tungstenite WebSocket configuration in place.
169    ///
170    /// Use this to adjust selected fields while keeping the remaining defaults.
171    pub fn websocket_config_mut<F: FnOnce(&mut WebSocketConfig)>(mut self, f: F) -> Self {
172        f(&mut self.config.websocket_config);
173        self
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use std::time::Duration;
180
181    use super::*;
182
183    #[test]
184    fn test_builder_configuration_chaining() {
185        let server = WsIoServer::builder()
186            .broadcast_concurrency_limit(1024)
187            .http_request_upgrade_timeout(Duration::from_millis(750))
188            .init_request_handler_timeout(Duration::from_secs(1))
189            .init_response_handler_timeout(Duration::from_secs(2))
190            .init_response_timeout(Duration::from_secs(3))
191            .middleware_execution_timeout(Duration::from_secs(4))
192            .on_close_handler_timeout(Duration::from_secs(5))
193            .on_connect_handler_timeout(Duration::from_secs(6))
194            .packet_codec(WsIoPacketCodec::Msgpack)
195            .request_path("/custom")
196            .websocket_config_mut(|config| {
197                *config = config.max_frame_size(Some(999));
198            })
199            .build();
200
201        // Access internal config through the built runtime
202        let config = &server.0.config;
203        assert_eq!(config.broadcast_concurrency_limit, 1024);
204        assert_eq!(config.http_request_upgrade_timeout, Duration::from_millis(750));
205        assert_eq!(config.init_request_handler_timeout, Duration::from_secs(1));
206        assert_eq!(config.init_response_handler_timeout, Duration::from_secs(2));
207        assert_eq!(config.init_response_timeout, Duration::from_secs(3));
208        assert_eq!(config.middleware_execution_timeout, Duration::from_secs(4));
209        assert_eq!(config.on_close_handler_timeout, Duration::from_secs(5));
210        assert_eq!(config.on_connect_handler_timeout, Duration::from_secs(6));
211        assert!(matches!(config.packet_codec, WsIoPacketCodec::Msgpack));
212        assert_eq!(config.request_path, "/custom");
213        assert_eq!(config.websocket_config.max_frame_size, Some(999));
214    }
215
216    #[test]
217    fn test_builder_websocket_config_override() {
218        let config = WebSocketConfig::default().max_frame_size(Some(42));
219        let server = WsIoServer::builder().websocket_config(config).build();
220        assert_eq!(server.0.config.websocket_config.max_frame_size, Some(42));
221    }
222}