workflow_websocket/client/
config.rs

1//!
2//! WebSocket client configuration options
3//!
4
5use super::{error::Error, result::Result, Handshake, Resolver};
6use cfg_if::cfg_if;
7use js_sys::Object;
8use std::sync::Arc;
9use wasm_bindgen::prelude::*;
10use workflow_wasm::extensions::object::*;
11
12///
13/// Configuration struct for WebSocket client (native Tungstenite and NodeJs connections only)
14///
15#[derive(Clone)]
16pub struct WebSocketConfig {
17    /// The target minimum size of the write buffer to reach before writing the data
18    /// to the underlying stream.
19    /// The default value is 128 KiB.
20    ///
21    /// If set to `0` each message will be eagerly written to the underlying stream.
22    /// It is often more optimal to allow them to buffer a little, hence the default value.
23    ///
24    /// Note: [`flush`](WebSocket::flush) will always fully write the buffer regardless.
25    pub write_buffer_size: usize,
26    /// The max size of the write buffer in bytes. Setting this can provide backpressure
27    /// in the case the write buffer is filling up due to write errors.
28    /// The default value is unlimited.
29    ///
30    /// Note: The write buffer only builds up past [`write_buffer_size`](Self::write_buffer_size)
31    /// when writes to the underlying stream are failing. So the **write buffer can not
32    /// fill up if you are not observing write errors even if not flushing**.
33    ///
34    /// Note: Should always be at least [`write_buffer_size + 1 message`](Self::write_buffer_size)
35    /// and probably a little more depending on error handling strategy.
36    pub max_write_buffer_size: usize,
37    /// The maximum size of a message. `None` means no size limit. The default value is 64 MiB
38    /// which should be reasonably big for all normal use-cases but small enough to prevent
39    /// memory eating by a malicious user.
40    pub max_message_size: Option<usize>,
41    /// The maximum size of a single message frame. `None` means no size limit. The limit is for
42    /// frame payload NOT including the frame header. The default value is 16 MiB which should
43    /// be reasonably big for all normal use-cases but small enough to prevent memory eating
44    /// by a malicious user.
45    pub max_frame_size: Option<usize>,
46    /// When set to `true`, the server will accept and handle unmasked frames
47    /// from the client. According to the RFC 6455, the server must close the
48    /// connection to the client in such cases, however it seems like there are
49    /// some popular libraries that are sending unmasked frames, ignoring the RFC.
50    /// By default this option is set to `false`, i.e. according to RFC 6455.
51    pub accept_unmasked_frames: bool,
52    /// The capacity of the channel used to queue incoming messages from WebSocket.
53    pub receiver_channel_cap: Option<usize>,
54    /// The capacity of the channel used to queue outgoing messages to WebSocket.
55    pub sender_channel_cap: Option<usize>,
56    /// Handshake handler for WebSocket connections. If supplied, it will be called
57    /// when the connection is established. The handshake handler can be used to
58    /// perform additional validation or setup before the connection is used.
59    pub handshake: Option<Arc<dyn Handshake>>,
60    /// Resolver for WebSocket connections. If supplied, it will be called to resolve
61    /// the URL before the connection is established. The resolver can be used as
62    /// an alternative to supplying the URL and will be invoked each time the
63    /// websocket needs to be connected or reconnected.
64    pub resolver: Option<Arc<dyn Resolver>>,
65}
66
67impl Default for WebSocketConfig {
68    fn default() -> Self {
69        WebSocketConfig {
70            write_buffer_size: 128 * 1024,
71            max_write_buffer_size: usize::MAX,
72            max_message_size: Some(64 << 20),
73            max_frame_size: Some(16 << 20),
74            accept_unmasked_frames: false,
75            receiver_channel_cap: None,
76            sender_channel_cap: None,
77            handshake: None,
78            resolver: None,
79        }
80    }
81}
82
83cfg_if! {
84    if #[cfg(feature = "wasm32-sdk")] {
85
86        #[wasm_bindgen(typescript_custom_section)]
87        const TS_WEBSOCKET_CONFIG: &'static str = r#"
88
89        /**
90         * `WebSocketConfig` is used to configure the `WebSocket`.
91         * 
92         * @category WebSocket
93         */
94        export interface IWebSocketConfig {
95            /** Maximum size of the WebSocket message. */
96            maxMessageSize: number,
97            /** Maximum size of the WebSocket frame. */
98            maxFrameSize: number,
99        }
100        "#;
101
102        #[wasm_bindgen]
103        extern "C" {
104            #[wasm_bindgen(extends = js_sys::Object, typescript_type = "IWebSocketConfig | undefined")]
105            pub type IWebSocketConfig;
106        }
107
108        impl TryFrom<IWebSocketConfig> for WebSocketConfig {
109            type Error = Error;
110            fn try_from(args: IWebSocketConfig) -> Result<Self> {
111                let config = if let Some(args) = args.dyn_ref::<Object>() {
112                    let mut config = WebSocketConfig::default();
113                    if let Some(max_frame_size) = args.get_value("maxFrameSize")?.as_f64() {
114                        config.max_frame_size = Some(max_frame_size as usize);
115                    }
116                    if let Some(max_message_size) = args.get_value("maxMessageSize")?.as_f64() {
117                        config.max_message_size = Some(max_message_size as usize);
118                    }
119                    config
120                } else {
121                    Default::default()
122                };
123                Ok(config)
124            }
125        }
126    }
127}
128
129pub(crate) struct WebSocketNodeJsConfig {
130    pub protocols: JsValue,
131    pub origin: JsValue,
132    pub headers: JsValue,
133    pub request_options: JsValue,
134    pub client_config: JsValue,
135}
136
137impl Default for WebSocketNodeJsConfig {
138    fn default() -> Self {
139        Self {
140            protocols: JsValue::UNDEFINED,
141            origin: JsValue::UNDEFINED,
142            headers: JsValue::UNDEFINED,
143            request_options: JsValue::UNDEFINED,
144            client_config: JsValue::UNDEFINED,
145        }
146    }
147}
148
149impl TryFrom<&WebSocketConfig> for WebSocketNodeJsConfig {
150    type Error = Error;
151    fn try_from(config: &WebSocketConfig) -> Result<Self> {
152        let client_config = Object::new();
153        if let Some(max_frame_size) = config.max_frame_size {
154            client_config.set("maxReceivedFrameSize", &JsValue::from(max_frame_size))?;
155        }
156        if let Some(max_message_size) = config.max_message_size {
157            client_config.set("maxReceivedMessageSize", &JsValue::from(max_message_size))?;
158        }
159
160        let nodejs_config = WebSocketNodeJsConfig {
161            protocols: JsValue::UNDEFINED,
162            origin: JsValue::UNDEFINED,
163            headers: JsValue::UNDEFINED,
164            request_options: JsValue::UNDEFINED,
165            client_config: client_config.into(),
166        };
167
168        Ok(nodejs_config)
169    }
170}