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}