lightstreamer_rs/connection/options.rs
1use crate::client::Transport;
2use crate::connection::management::{HeartbeatConfig, ReconnectionConfig};
3use crate::utils::{LightstreamerError, Proxy};
4use std::collections::HashMap;
5use std::fmt::{self, Debug, Formatter};
6
7/// Used by LightstreamerClient to provide an extra connection properties data object.
8/// Data struct that contains the policy settings used to connect to a Lightstreamer Server.
9/// An instance of this struct is attached to every LightstreamerClient as connection_options.
10///
11/// See also `LightstreamerClient`
12pub struct ConnectionOptions {
13 content_length: Option<u64>,
14 first_retry_max_delay: u64,
15 forced_transport: Option<Transport>,
16 http_extra_headers: Option<HashMap<String, String>>,
17 http_extra_headers_on_session_creation_only: bool,
18 idle_timeout: u64,
19 keepalive_interval: u64,
20 polling_interval: u64,
21 proxy: Option<Proxy>,
22 real_max_bandwidth: Option<u64>,
23 reconnect_timeout: u64,
24 requested_max_bandwidth: Option<f64>,
25 retry_delay: u64,
26 reverse_heartbeat_interval: u64,
27 server_instance_address_ignored: bool,
28 session_recovery_timeout: u64,
29 slowing_enabled: bool,
30 stalled_timeout: u64,
31 send_sync: bool,
32 _reduce_head: bool,
33 supported_diffs: Option<String>,
34 polling: bool,
35 ttl_millis: Option<u64>,
36 /// Configuration for automatic reconnection behavior
37 reconnection_config: ReconnectionConfig,
38 /// Configuration for heartbeat monitoring
39 heartbeat_config: HeartbeatConfig,
40 /// Whether automatic reconnection is enabled
41 auto_reconnect_enabled: bool,
42}
43
44impl ConnectionOptions {
45 /// Creates a new instance of `ConnectionOptions` with default values.
46 pub fn new() -> Self {
47 ConnectionOptions {
48 content_length: None,
49 first_retry_max_delay: 100,
50 forced_transport: None,
51 http_extra_headers: None,
52 http_extra_headers_on_session_creation_only: false,
53 idle_timeout: 19000,
54 keepalive_interval: 0,
55 polling_interval: 0,
56 proxy: None,
57 real_max_bandwidth: None,
58 reconnect_timeout: 3000,
59 requested_max_bandwidth: None,
60 retry_delay: 4000,
61 reverse_heartbeat_interval: 0,
62 session_recovery_timeout: 15000,
63 slowing_enabled: false,
64 stalled_timeout: 2000,
65 server_instance_address_ignored: false,
66 send_sync: true,
67 _reduce_head: false,
68 supported_diffs: None,
69 polling: false,
70 ttl_millis: None,
71 reconnection_config: ReconnectionConfig::default(),
72 heartbeat_config: HeartbeatConfig::default(),
73 auto_reconnect_enabled: false,
74 }
75 }
76
77 /// Inquiry method that gets the length expressed in bytes to be used by the Server for the
78 /// response body on a HTTP stream connection.
79 ///
80 /// # Returns
81 ///
82 /// The length to be used by the Server for the response body on a HTTP stream connection
83 ///
84 /// See also `setContentLength()`
85 pub fn get_content_length(&self) -> Option<u64> {
86 self.content_length
87 }
88
89 /// Inquiry method that gets the maximum time to wait before trying a new connection to the
90 /// Server in case the previous one is unexpectedly closed while correctly working.
91 ///
92 /// # Returns
93 ///
94 /// The max time (in milliseconds) to wait before trying a new connection.
95 ///
96 /// See also `setFirstRetryMaxDelay()`
97 pub fn get_first_retry_max_delay(&self) -> u64 {
98 self.first_retry_max_delay
99 }
100
101 /// Inquiry method that gets the value of the forced transport (if any).
102 ///
103 /// # Returns
104 ///
105 /// The forced transport or `None`
106 ///
107 /// See also `setForcedTransport()`
108 pub fn get_forced_transport(&self) -> Option<&Transport> {
109 self.forced_transport.as_ref()
110 }
111
112 /// Inquiry method that gets the Map object containing the extra headers to be sent to the server.
113 ///
114 /// # Returns
115 ///
116 /// The Map object containing the extra headers to be sent
117 ///
118 /// See also `setHttpExtraHeaders()`
119 ///
120 /// See also `setHttpExtraHeadersOnSessionCreationOnly()`
121 pub fn get_http_extra_headers(&self) -> Option<&HashMap<String, String>> {
122 self.http_extra_headers.as_ref()
123 }
124
125 /// Inquiry method that gets the maximum time the Server is allowed to wait for any data to be
126 /// sent in response to a polling request, if none has accumulated at request time. The wait
127 /// time used by the Server, however, may be different, because of server side restrictions.
128 ///
129 /// # Returns
130 ///
131 /// The time (in milliseconds) the Server is allowed to wait for data to send upon polling requests.
132 ///
133 /// See also `setIdleTimeout()`
134 pub fn get_idle_timeout(&self) -> u64 {
135 self.idle_timeout
136 }
137
138 /// Inquiry method that gets the interval between two keepalive packets sent by Lightstreamer
139 /// Server on a stream connection when no actual data is being transmitted. If the returned
140 /// value is 0, it means that the interval is to be decided by the Server upon the next connection.
141 ///
142 /// If the value has just been set and a connection to Lightstreamer Server has not been established
143 /// yet, the returned value is the time that is being requested to the Server. Afterwards, the
144 /// returned value is the time used by the Server, that may be different, because of Server side
145 /// constraints.
146 ///
147 /// # Returns
148 ///
149 /// The time, expressed in milliseconds, between two keepalive packets sent by the Server, or 0.
150 ///
151 /// See also `setKeepaliveInterval()`
152 pub fn get_keepalive_interval(&self) -> u64 {
153 self.keepalive_interval
154 }
155
156 /// Inquiry method that gets the polling interval used for polling connections.
157 ///
158 /// If the value has just been set and a polling request to Lightstreamer Server has not been
159 /// performed yet, the returned value is the polling interval that is being requested to the
160 /// Server. Afterwards, the returned value is the the time between subsequent polling requests
161 /// that is really allowed by the Server, that may be different, because of Server side constraints.
162 ///
163 /// # Returns
164 ///
165 /// The time (in milliseconds) between subsequent polling requests.
166 ///
167 /// See also `setPollingInterval()`
168 pub fn get_polling_interval(&self) -> u64 {
169 self.polling_interval
170 }
171
172 /// Inquiry method that gets the maximum bandwidth that can be consumed for the data coming
173 /// from Lightstreamer Server. This is the actual maximum bandwidth, in contrast with the requested
174 /// maximum bandwidth, returned by `get_requested_max_bandwidth()`.
175 ///
176 /// The value may differ from the requested one because of restrictions operated on the server
177 /// side, or because bandwidth management is not supported (in this case it is always "unlimited"),
178 /// but also because of number rounding.
179 ///
180 /// If a connection to Lightstreamer Server is not currently active, `None` is returned; soon
181 /// after the connection is established, the value becomes available, as notified by a call to
182 /// `ClientListener.onPropertyChange()` with argument "realMaxBandwidth".
183 ///
184 /// # Returns
185 ///
186 /// A decimal number, which represents the maximum bandwidth applied by the Server for the streaming
187 /// or polling connection expressed in kbps (kilobits/sec), or the string "unlimited", or `None`.
188 ///
189 /// See also `setRequestedMaxBandwidth()`
190 pub fn get_real_max_bandwidth(&self) -> Option<f64> {
191 // Implementation to get the actual maximum bandwidth from the server
192 unimplemented!()
193 }
194
195 /// Inquiry method that gets the time the client, after entering "STALLED" status, is allowed
196 /// to keep waiting for a keepalive packet or any data on a stream connection, before disconnecting
197 /// and trying to reconnect to the Server.
198 ///
199 /// # Returns
200 ///
201 /// The idle time (in milliseconds) admitted in "STALLED" status before trying to reconnect
202 /// to the Server.
203 ///
204 /// See also `setReconnectTimeout()`
205 pub fn get_reconnect_timeout(&self) -> u64 {
206 self.reconnect_timeout
207 }
208
209 /// Inquiry method that gets the maximum bandwidth that can be consumed for the data coming
210 /// from Lightstreamer Server, as requested for this session. The maximum bandwidth limit really
211 /// applied by the Server on the session is provided by `get_real_max_bandwidth()`
212 ///
213 /// # Returns
214 ///
215 /// A decimal number, which represents the maximum bandwidth requested for the streaming or polling
216 /// connection expressed in kbps (kilobits/sec), or the string "unlimited".
217 ///
218 /// See also `setRequestedMaxBandwidth()`
219 pub fn get_requested_max_bandwidth(&self) -> Option<f64> {
220 self.requested_max_bandwidth
221 }
222
223 /// Inquiry method that gets the minimum time to wait before trying a new connection to the
224 /// Server in case the previous one failed for any reason, which is also the maximum time to
225 /// wait for a response to a request before dropping the connection and trying with a different
226 /// approach. Note that the delay is calculated from the moment the effort to create a connection
227 /// is made, not from the moment the failure is detected or the connection timeout expires.
228 ///
229 /// # Returns
230 ///
231 /// The time (in milliseconds) to wait before trying a new connection.
232 ///
233 /// See also `setRetryDelay()`
234 pub fn get_retry_delay(&self) -> u64 {
235 self.retry_delay
236 }
237
238 /// Inquiry method that gets the reverse-heartbeat interval expressed in milliseconds. A 0 value
239 /// is possible, meaning that the mechanism is disabled.
240 ///
241 /// # Returns
242 ///
243 /// The reverse-heartbeat interval, or 0.
244 ///
245 /// See also `setReverseHeartbeatInterval()`
246 pub fn get_reverse_heartbeat_interval(&self) -> u64 {
247 self.reverse_heartbeat_interval
248 }
249
250 /// Inquiry method that gets if LS_send_sync is to be sent to the server.
251 /// If set to false, instructs the Server not to send the SYNC notifications on this connection.
252 /// If omitted, the default is true.
253 pub fn get_send_sync(&self) -> bool {
254 self.send_sync
255 }
256
257 /// Inquiry method that gets the maximum time allowed for attempts to recover the current session
258 /// upon an interruption, after which a new session will be created. A 0 value also means that
259 /// any attempt to recover the current session is prevented in the first place.
260 ///
261 /// # Returns
262 ///
263 /// The maximum time allowed for recovery attempts, possibly 0.
264 ///
265 /// See also `setSessionRecoveryTimeout()`
266 pub fn get_session_recovery_timeout(&self) -> u64 {
267 self.session_recovery_timeout
268 }
269
270 /// Inquiry method that gets the extra time the client can wait when an expected keepalive packet
271 /// has not been received on a stream connection (and no actual data has arrived), before entering
272 /// the "STALLED" status.
273 ///
274 /// # Returns
275 ///
276 /// The idle time (in milliseconds) admitted before entering the "STALLED" status.
277 ///
278 /// See also `setStalledTimeout()`
279 pub fn get_stalled_timeout(&self) -> u64 {
280 self.stalled_timeout
281 }
282
283 /// Inquiry method that checks if the restriction on the forwarding of the configured extra
284 /// http headers applies or not.
285 ///
286 /// # Returns
287 ///
288 /// `true`/`false` if the restriction applies or not.
289 ///
290 /// See also `setHttpExtraHeadersOnSessionCreationOnly()`
291 ///
292 /// See also `setHttpExtraHeaders()`
293 pub fn is_http_extra_headers_on_session_creation_only(&self) -> bool {
294 self.http_extra_headers_on_session_creation_only
295 }
296
297 /// Inquiry method that checks if the client is going to ignore the server instance address
298 /// that will possibly be sent by the server.
299 ///
300 /// # Returns
301 ///
302 /// Whether or not to ignore the server instance address sent by the server.
303 ///
304 /// See also `setServerInstanceAddressIgnored()`
305 pub fn is_server_instance_address_ignored(&self) -> bool {
306 self.server_instance_address_ignored
307 }
308
309 /// Inquiry method that checks if the slowing algorithm is enabled or not.
310 ///
311 /// # Returns
312 ///
313 /// Whether the slowing algorithm is enabled or not.
314 ///
315 /// See also `setSlowingEnabled()`
316 pub fn is_slowing_enabled(&self) -> bool {
317 self.slowing_enabled
318 }
319
320 /// Setter method that sets the length in bytes to be used by the Server for the response body
321 /// on a stream connection (a minimum length, however, is ensured by the server). After the
322 /// content length exhaustion, the connection will be closed and a new bind connection will
323 /// be automatically reopened.
324 ///
325 /// NOTE that this setting only applies to the "HTTP-STREAMING" case (i.e. not to WebSockets).
326 ///
327 /// A length decided by the library, to ensure the best performance. It can be of a few MB
328 /// or much higher, depending on the environment.
329 ///
330 /// The content length should be set before calling the `LightstreamerClient.connect()` method.
331 /// However, the value can be changed at any time: the supplied value will be used for the
332 /// next streaming connection (either a bind or a brand new session).
333 ///
334 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
335 /// with argument "contentLength" on any `ClientListener` listening to the related `LightstreamerClient`.
336 ///
337 /// # Parameters
338 ///
339 /// * `content_length`: The length to be used by the Server for the response body on a HTTP
340 /// stream connection.
341 ///
342 /// # Raises
343 ///
344 /// * `LightstreamerError`: if zero is configured (value must be > 0)
345 pub fn set_content_length(&mut self, content_length: u64) -> Result<(), LightstreamerError> {
346 if content_length == 0 {
347 return Err(LightstreamerError::invalid_argument(
348 "Content length cannot be zero",
349 ));
350 }
351
352 self.content_length = Some(content_length);
353 Ok(())
354 }
355
356 /// Setter method that sets the maximum time to wait before trying a new connection to the Server
357 /// in case the previous one is unexpectedly closed while correctly working. The new connection
358 /// may be either the opening of a new session or an attempt to recovery the current session,
359 /// depending on the kind of interruption.
360 ///
361 /// The actual delay is a randomized value between 0 and this value. This randomization might
362 /// help avoid a load spike on the cluster due to simultaneous reconnections, should one of
363 /// the active servers be stopped. Note that this delay is only applied before the first reconnection:
364 /// should such reconnection fail, only the setting of `setRetryDelay()` will be applied.
365 ///
366 /// 100 (0.1 seconds)
367 ///
368 /// This value can be set and changed at any time.
369 ///
370 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
371 /// with argument "firstRetryMaxDelay" on any `ClientListener` listening to the related `LightstreamerClient`.
372 ///
373 /// # Parameters
374 ///
375 /// * `first_retry_max_delay`: The max time (in milliseconds) to wait before trying a new connection.
376 ///
377 /// # Raises
378 ///
379 /// * `LightstreamerError`: if zero is configured (value must be > 0)
380 pub fn set_first_retry_max_delay(
381 &mut self,
382 first_retry_max_delay: u64,
383 ) -> Result<(), LightstreamerError> {
384 if first_retry_max_delay == 0 {
385 return Err(LightstreamerError::invalid_argument(
386 "First retry max delay cannot be zero",
387 ));
388 }
389
390 self.first_retry_max_delay = first_retry_max_delay;
391 Ok(())
392 }
393
394 /// Setter method that can be used to disable/enable the Stream-Sense algorithm and to force
395 /// the client to use a fixed transport or a fixed combination of a transport and a connection
396 /// type. When a combination is specified the Stream-Sense algorithm is completely disabled.
397 ///
398 /// The method can be used to switch between streaming and polling connection types and between
399 /// HTTP and WebSocket transports.
400 ///
401 /// In some cases, the requested status may not be reached, because of connection or environment
402 /// problems. In that case the client will continuously attempt to reach the configured status.
403 ///
404 /// Note that if the Stream-Sense algorithm is disabled, the client may still enter the "CONNECTED:STREAM-SENSING"
405 /// status; however, in that case, if it eventually finds out that streaming is not possible,
406 /// no recovery will be tried.
407 ///
408 /// None (full Stream-Sense enabled).
409 ///
410 /// This method can be called at any time. If called while the client is connecting or connected
411 /// it will instruct to switch connection type to match the given configuration.
412 ///
413 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
414 /// with argument "forcedTransport" on any `ClientListener` listening to the related `LightstreamerClient`.
415 ///
416 /// # Parameters
417 ///
418 /// * `forced_transport`:
419 /// - `None`: the Stream-Sense algorithm is enabled and the client will automatically connect
420 /// using the most appropriate transport and connection type among those made possible by
421 /// the environment.
422 /// - `"WS"`: the Stream-Sense algorithm is enabled as in the `None` case but the client will
423 /// only use WebSocket based connections. If a connection over WebSocket is not possible
424 /// because of the environment the client will not connect at all.
425 /// - `"HTTP"`: the Stream-Sense algorithm is enabled as in the `None` case but the client
426 /// will only use HTTP based connections. If a connection over HTTP is not possible because
427 /// of the environment the client will not connect at all.
428 /// - `"WS-STREAMING"`: the Stream-Sense algorithm is disabled and the client will only connect
429 /// on Streaming over WebSocket. If Streaming over WebSocket is not possible because of
430 /// the environment the client will not connect at all.
431 /// - `"HTTP-STREAMING"`: the Stream-Sense algorithm is disabled and the client will only
432 /// connect on Streaming over HTTP. If Streaming over HTTP is not possible because of the
433 /// browser/environment the client will not connect at all.
434 /// - `"WS-POLLING"`: the Stream-Sense algorithm is disabled and the client will only connect
435 /// on Polling over WebSocket. If Polling over WebSocket is not possible because of the
436 /// environment the client will not connect at all.
437 /// - `"HTTP-POLLING"`: the Stream-Sense algorithm is disabled and the client will only connect
438 /// on Polling over HTTP. If Polling over HTTP is not possible because of the environment
439 /// the client will not connect at all.
440 ///
441 /// # Raises
442 ///
443 /// * `LightstreamerError`: if the given value is not in the list of the admitted ones.
444 pub fn set_forced_transport(&mut self, forced_transport: Option<Transport>) {
445 self.forced_transport = forced_transport;
446 }
447
448 /// Setter method that enables/disables the setting of extra HTTP headers to all the request
449 /// performed to the Lightstreamer server by the client.
450 ///
451 /// Note that the Content-Type header is reserved by the client library itself, while other
452 /// headers might be refused by the environment and others might cause the connection to the
453 /// server to fail.
454 ///
455 /// For instance, you cannot use this method to specify custom cookies to be sent to Lightstreamer
456 /// Server; leverage `LightstreamerClient.addCookies()` instead. The use of custom headers
457 /// might also cause the client to send an OPTIONS request to the server before opening the
458 /// actual connection.
459 ///
460 /// None (meaning no extra headers are sent).
461 ///
462 /// This setting should be performed before calling the `LightstreamerClient.connect()` method.
463 /// However, the value can be changed at any time: the supplied value will be used for the
464 /// next HTTP request or WebSocket establishment.
465 ///
466 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
467 /// with argument "httpExtraHeaders" on any `ClientListener` listening to the related `LightstreamerClient`.
468 ///
469 /// # Parameters
470 ///
471 /// * `http_extra_headers`: a Map object containing header-name header-value pairs. `None`
472 /// can be specified to avoid extra headers to be sent.
473 pub fn set_http_extra_headers(&mut self, http_extra_headers: Option<HashMap<String, String>>) {
474 self.http_extra_headers = http_extra_headers;
475 }
476
477 /// Setter method that enables/disables a restriction on the forwarding of the extra http headers
478 /// specified through `setHttpExtraHeaders()`. If true, said headers will only be sent during
479 /// the session creation process (and thus will still be available to the metadata adapter
480 /// notifyUser method) but will not be sent on following requests. On the contrary, when set
481 /// to true, the specified extra headers will be sent to the server on every request.
482 ///
483 /// false
484 ///
485 /// This setting should be performed before calling the `LightstreamerClient.connect()` method.
486 /// However, the value can be changed at any time: the supplied value will be used for the
487 /// next HTTP request or WebSocket establishment.
488 ///
489 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
490 /// with argument "httpExtraHeadersOnSessionCreationOnly" on any `ClientListener` listening
491 /// to the related `LightstreamerClient`.
492 ///
493 /// # Parameters
494 ///
495 /// * `http_extra_headers_on_session_creation_only`: `true`/`false` to enable/disable the restriction
496 /// on extra headers forwarding.
497 pub fn set_http_extra_headers_on_session_creation_only(
498 &mut self,
499 http_extra_headers_on_session_creation_only: bool,
500 ) {
501 self.http_extra_headers_on_session_creation_only =
502 http_extra_headers_on_session_creation_only;
503 }
504
505 /// Setter method that sets the maximum time the Server is allowed to wait for any data to
506 /// be sent in response to a polling request, if none has accumulated at request time. Setting
507 /// this time to a nonzero value and the polling interval to zero leads to an "asynchronous
508 /// polling" behavior, which, on low data rates, is very similar to the streaming case. Setting
509 /// this time to zero and the polling interval to a nonzero value, on the other hand, leads
510 /// to a classical "synchronous polling".
511 ///
512 /// Note that the Server may, in some cases, delay the answer for more than the supplied time,
513 /// to protect itself against a high polling rate or because of bandwidth restrictions. Also,
514 /// the Server may impose an upper limit on the wait time, in order to be able to check for
515 /// client-side connection drops.
516 ///
517 /// 19000 (19 seconds).
518 ///
519 /// The idle timeout should be set before calling the `LightstreamerClient.connect()` method.
520 /// However, the value can be changed at any time: the supplied value will be used for the
521 /// next polling request.
522 ///
523 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
524 /// with argument "idleTimeout" on any `ClientListener` listening to the related `LightstreamerClient`.
525 ///
526 /// # Parameters
527 ///
528 /// * `idle_timeout`: The time (in milliseconds) the Server is allowed to wait for data to
529 /// send upon polling requests.
530 ///
531 /// # Raises
532 ///
533 /// * `LightstreamerError`: if zero is configured (value must be > 0)
534 pub fn set_idle_timeout(&mut self, idle_timeout: u64) -> Result<(), LightstreamerError> {
535 if idle_timeout == 0 {
536 return Err(LightstreamerError::invalid_argument(
537 "Idle timeout cannot be zero",
538 ));
539 }
540
541 self.idle_timeout = idle_timeout;
542 Ok(())
543 }
544
545 /// Setter method that sets the interval between two keepalive packets to be sent by Lightstreamer
546 /// Server on a stream connection when no actual data is being transmitted. The Server may,
547 /// however, impose a lower limit on the keepalive interval, in order to protect itself. Also,
548 /// the Server may impose an upper limit on the keepalive interval, in order to be able to
549 /// check for client-side connection drops. If 0 is specified, the interval will be decided
550 /// by the Server.
551 ///
552 /// 0 (meaning that the Server will send keepalive packets based on its own configuration).
553 ///
554 /// The keepalive interval should be set before calling the `LightstreamerClient.connect()`
555 /// method. However, the value can be changed at any time: the supplied value will be used
556 /// for the next streaming connection (either a bind or a brand new session). Note that, after
557 /// a connection, the value may be changed to the one imposed by the Server.
558 ///
559 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
560 /// with argument "keepaliveInterval" on any `ClientListener` listening to the related `LightstreamerClient`.
561 ///
562 /// # Parameters
563 ///
564 /// * `keepalive_interval`: the keepalive interval time (in milliseconds) to set, or 0.
565 ///
566 /// # Raises
567 ///
568 /// * `LightstreamerError`: if zero is configured (value must be > 0)
569 ///
570 /// See also `setStalledTimeout()`
571 ///
572 /// See also `setReconnectTimeout()`
573 pub fn set_keepalive_interval(
574 &mut self,
575 keepalive_interval: u64,
576 ) -> Result<(), LightstreamerError> {
577 if keepalive_interval == 0 {
578 self.keepalive_interval = keepalive_interval;
579 return Ok(());
580 }
581
582 if keepalive_interval < self.stalled_timeout || keepalive_interval < self.reconnect_timeout
583 {
584 return Err(LightstreamerError::invalid_argument(
585 "Keepalive interval should be greater than or equal to stalled timeout and reconnect timeout",
586 ));
587 }
588
589 self.keepalive_interval = keepalive_interval;
590 Ok(())
591 }
592
593 /// Setter method that sets the polling interval used for polling connections. The client
594 /// switches from the default streaming mode to polling mode when the client network infrastructure
595 /// does not allow streaming. Also, polling mode can be forced by calling `setForcedTransport()`
596 /// with "WS-POLLING" or "HTTP-POLLING" as parameter.
597 ///
598 /// The polling interval affects the rate at which polling requests are issued. It is the time
599 /// between the start of a polling request and the start of the next request. However, if the
600 /// polling interval expires before the first polling request has returned, then the second
601 /// polling request is delayed. This may happen, for instance, when the Server delays the answer
602 /// because of the idle timeout setting. In any case, the polling interval allows for setting
603 /// an upper limit on the polling frequency.
604 ///
605 /// The Server does not impose a lower limit on the client polling interval. However, in some
606 /// cases, it may protect itself against a high polling rate by delaying its answer. Network
607 /// limitations and configured bandwidth limits may also lower the polling rate, despite of
608 /// the client polling interval.
609 ///
610 /// The Server may, however, impose an upper limit on the polling interval, in order to be
611 /// able to promptly detect terminated polling request sequences and discard related session
612 /// information.
613 ///
614 /// 0 (pure "asynchronous polling" is configured).
615 ///
616 /// The polling interval should be set before calling the `LightstreamerClient.connect()` method.
617 /// However, the value can be changed at any time: the supplied value will be used for the
618 /// next polling request.
619 ///
620 /// Note that, after each polling request, the value may be changed to the one imposed by the
621 /// Server.
622 ///
623 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
624 /// with argument "pollingInterval" on any `ClientListener` listening to the related `LightstreamerClient`.
625 ///
626 /// # Parameters
627 ///
628 /// * `polling_interval`: The time (in milliseconds) between subsequent polling requests. Zero
629 /// is a legal value too, meaning that the client will issue a new polling request as soon
630 /// as a previous one has returned.
631 ///
632 /// # Raises
633 ///
634 /// * `LightstreamerError`: if the value violates timeout constraints
635 pub fn set_polling_interval(
636 &mut self,
637 polling_interval: u64,
638 ) -> Result<(), LightstreamerError> {
639 if polling_interval == 0 {
640 self.polling_interval = polling_interval;
641 return Ok(());
642 }
643
644 if polling_interval < self.idle_timeout {
645 return Err(LightstreamerError::invalid_argument(
646 "Polling interval should be greater than or equal to idle timeout",
647 ));
648 }
649
650 self.polling_interval = polling_interval;
651 Ok(())
652 }
653
654 /// Setter method that configures the coordinates to a proxy server to be used to connect
655 /// to the Lightstreamer Server.
656 ///
657 /// None (meaning not to pass through a proxy).
658 ///
659 /// This value can be set and changed at any time. The supplied value will be used for the
660 /// next connection attempt.
661 ///
662 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
663 /// with argument "proxy" on any `ClientListener` listening to the related `LightstreamerClient`.
664 ///
665 /// # Parameters
666 ///
667 /// * `proxy`: The proxy configuration. Specify `None` to avoid using a proxy.
668 pub fn set_proxy(&mut self, proxy: Option<Proxy>) {
669 self.proxy = proxy;
670 }
671
672 /// Setter method that sets the time the client, after entering "STALLED" status, is allowed
673 /// to keep waiting for a keepalive packet or any data on a stream connection, before disconnecting
674 /// and trying to reconnect to the Server. The new connection may be either the opening of
675 /// a new session or an attempt to recovery the current session, depending on the kind of
676 /// interruption.
677 ///
678 /// 3000 (3 seconds).
679 ///
680 /// This value can be set and changed at any time.
681 ///
682 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
683 /// with argument "reconnectTimeout" on any `ClientListener` listening to the related `LightstreamerClient`.
684 ///
685 /// # Parameters
686 ///
687 /// * `reconnect_timeout`: The idle time (in milliseconds) allowed in "STALLED" status before
688 /// trying to reconnect to the Server.
689 ///
690 /// # Raises
691 ///
692 /// * `LightstreamerError`: if zero is configured (value must be > 0)
693 ///
694 /// See also `setStalledTimeout()`
695 ///
696 /// See also `setKeepaliveInterval()`
697 pub fn set_reconnect_timeout(
698 &mut self,
699 reconnect_timeout: u64,
700 ) -> Result<(), LightstreamerError> {
701 if reconnect_timeout == 0 {
702 return Err(LightstreamerError::invalid_argument(
703 "Reconnect timeout cannot be zero",
704 ));
705 }
706 self.reconnect_timeout = reconnect_timeout;
707 Ok(())
708 }
709
710 /// Setter method that sets the maximum bandwidth expressed in kilobits/s that can be consumed
711 /// for the data coming from Lightstreamer Server. A limit on bandwidth may already be posed
712 /// by the Metadata Adapter, but the client can furtherly restrict this limit. The limit applies
713 /// to the bytes received in each streaming or polling connection.
714 ///
715 /// Bandwidth Control is an optional feature, available depending on Edition and License Type.
716 /// To know what features are enabled by your license, please see the License tab of the Monitoring
717 /// Dashboard (by default, available at /dashboard).
718 ///
719 /// "unlimited"
720 ///
721 /// The bandwidth limit can be set and changed at any time. If a connection is currently active,
722 /// the bandwidth limit for the connection is changed on the fly. Remember that the Server
723 /// may apply a different limit.
724 ///
725 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
726 /// with argument "requestedMaxBandwidth" on any `ClientListener` listening to the related
727 /// `LightstreamerClient`. Moreover, upon any change or attempt to change the limit, the Server
728 /// will notify the client and such notification will be received through a call to `ClientListener.onPropertyChange()`
729 /// with argument "realMaxBandwidth" on any `ClientListener` listening to the related `LightstreamerClient`.
730 ///
731 /// # Parameters
732 ///
733 /// * `max_bandwidth`: A decimal number, which represents the maximum bandwidth requested for
734 /// the streaming or polling connection expressed in kbps (kilobits/sec). The string "unlimited"
735 /// is also allowed, to mean that the maximum bandwidth can be entirely decided on the Server
736 /// side (the check is case insensitive).
737 ///
738 /// # Raises
739 ///
740 /// * `LightstreamerError`: if zero or an invalid value (excluding special values) is passed.
741 ///
742 /// See also `get_real_max_bandwidth()`
743 pub fn set_requested_max_bandwidth(
744 &mut self,
745 max_bandwidth: Option<f64>,
746 ) -> Result<(), LightstreamerError> {
747 if let Some(bandwidth) = max_bandwidth
748 && bandwidth <= 0.0
749 {
750 return Err(LightstreamerError::invalid_argument(
751 "Maximum bandwidth should be a positive number or 'unlimited'",
752 ));
753 }
754
755 self.requested_max_bandwidth = max_bandwidth;
756 Ok(())
757 }
758
759 /// Setter method that sets
760 ///
761 /// 1. the minimum time to wait before trying a new connection to the Server in case the previous
762 /// one failed for any reason; and
763 /// 2. the maximum time to wait for a response to a request before dropping the connection
764 /// and trying with a different approach.
765 ///
766 /// Enforcing a delay between reconnections prevents strict loops of connection attempts when
767 /// these attempts always fail immediately because of some persisting issue. This applies both
768 /// to reconnections aimed at opening a new session and to reconnections aimed at attempting
769 /// a recovery of the current session.
770 ///
771 /// Note that the delay is calculated from the moment the effort to create a connection is
772 /// made, not from the moment the failure is detected. As a consequence, when a working connection
773 /// is interrupted, this timeout is usually already consumed and the new attempt can be immediate
774 /// (except that `ConnectionOptions.setFirstRetryMaxDelay()` will apply in this case). As another
775 /// consequence, when a connection attempt gets no answer and times out, the new attempt will
776 /// be immediate.
777 ///
778 /// As a timeout on unresponsive connections, it is applied in these cases:
779 ///
780 /// - Streaming: Applied on any attempt to setup the streaming connection. If after the timeout
781 /// no data has arrived on the stream connection, the client may automatically switch transport
782 /// or may resort to a polling connection.
783 /// - Polling and pre-flight requests: Applied on every connection. If after the timeout no
784 /// data has arrived on the polling connection, the entire connection process restarts from
785 /// scratch.
786 ///
787 /// This setting imposes only a minimum delay. In order to avoid network congestion, the library
788 /// may use a longer delay if the issue preventing the establishment of a session persists.
789 ///
790 /// 4000 (4 seconds).
791 ///
792 /// This value can be set and changed at any time.
793 ///
794 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
795 /// with argument "retryDelay" on any `ClientListener` listening to the related `LightstreamerClient`.
796 ///
797 /// # Parameters
798 ///
799 /// * `retry_delay`: The time (in milliseconds) to wait before trying a new connection.
800 ///
801 /// # Raises
802 ///
803 /// * `LightstreamerError`: if zero is configured (value must be > 0)
804 ///
805 /// See also `setFirstRetryMaxDelay()`
806 pub fn set_retry_delay(&mut self, retry_delay: u64) -> Result<(), LightstreamerError> {
807 if retry_delay == 0 {
808 return Err(LightstreamerError::invalid_argument(
809 "Retry delay cannot be zero",
810 ));
811 }
812
813 self.retry_delay = retry_delay;
814 Ok(())
815 }
816
817 /// Setter method that enables/disables the reverse-heartbeat mechanism by setting the heartbeat
818 /// interval. If the given value (expressed in milliseconds) equals 0 then the reverse-heartbeat
819 /// mechanism will be disabled; otherwise if the given value is greater than 0 the mechanism
820 /// will be enabled with the specified interval.
821 ///
822 /// When the mechanism is active, the client will ensure that there is at most the specified
823 /// interval between a control request and the following one, by sending empty control requests
824 /// (the "reverse heartbeats") if necessary.
825 ///
826 /// This can serve various purposes:
827 ///
828 /// 1. Preventing the communication infrastructure from closing an inactive socket that is ready
829 /// for reuse for more HTTP control requests, to avoid connection reestablishment overhead.
830 /// However it is not guaranteed that the connection will be kept open,as the underlying
831 /// TCP implementation may open a new socket each time a HTTP request needs to be sent.
832 /// Note that this will be done only when a session is in place.
833 /// 2. Allowing the Server to detect when a streaming connection or Websocket is interrupted
834 /// but not closed. In these cases, the client eventually closes the connection, but the
835 /// Server cannot see that (the connection remains "half-open") and just keeps trying to
836 /// write. This is done by notifying the timeout to the Server upon each streaming request.
837 /// For long polling, the `setIdleTimeout()` setting has a similar function.
838 /// 3. Allowing the Server to detect cases in which the client has closed a connection in HTTP
839 /// streaming, but the socket is kept open by some intermediate node, which keeps consuming
840 /// the response. This is also done by notifying the timeout to the Server upon each streaming
841 /// request, whereas, for long polling, the `setIdleTimeout()` setting has a similar function.
842 ///
843 /// 0 (meaning that the mechanism is disabled).
844 ///
845 /// This setting should be performed before calling the `LightstreamerClient.connect()` method.
846 /// However, the value can be changed at any time: the setting will be obeyed immediately,
847 /// unless a higher heartbeat frequency was notified to the Server for the current connection.
848 /// The setting will always be obeyed upon the next connection (either a bind or a brand new
849 /// session).
850 ///
851 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
852 /// with argument "reverseHeartbeatInterval" on any `ClientListener` listening to the related
853 /// `LightstreamerClient`.
854 ///
855 /// # Parameters
856 ///
857 /// * `reverse_heartbeat_interval`: the interval, expressed in milliseconds, between subsequent
858 /// reverse-heartbeats, or 0.
859 ///
860 /// # Raises
861 ///
862 /// * `LightstreamerError`: if the value is invalid
863 pub fn set_reverse_heartbeat_interval(
864 &mut self,
865 reverse_heartbeat_interval: u64,
866 ) -> Result<(), LightstreamerError> {
867 if reverse_heartbeat_interval == 0 {
868 self.reverse_heartbeat_interval = reverse_heartbeat_interval;
869 return Ok(());
870 }
871
872 if reverse_heartbeat_interval < self.retry_delay {
873 return Err(LightstreamerError::invalid_argument(
874 "Reverse heartbeat interval should be greater than or equal to retry delay",
875 ));
876 }
877
878 self.reverse_heartbeat_interval = reverse_heartbeat_interval;
879 Ok(())
880 }
881
882 /// Setter method that can be used to disable/enable the automatic handling of server instance
883 /// address that may be returned by the Lightstreamer server during session creation.
884 ///
885 /// In fact, when a Server cluster is in place, the Server address specified through `ConnectionDetails.setServerAddress()`
886 /// can identify various Server instances; in order to ensure that all requests related to
887 /// a session are issued to the same Server instance, the Server can answer to the session
888 /// opening request by providing an address which uniquely identifies its own instance.
889 ///
890 /// Setting this value to true permits to ignore that address and to always connect through
891 /// the address supplied in `setServerAddress`. This may be needed in a test environment,
892 /// if the Server address specified is actually a local address to a specific Server instance
893 /// in the cluster.
894 ///
895 /// Server Clustering is an optional feature, available depending on Edition and License Type.
896 /// To know what features are enabled by your license, please see the License tab of the Monitoring
897 /// Dashboard (by default, available at /dashboard).
898 ///
899 /// false.
900 ///
901 /// This method can be called at any time. If called while connected, it will be applied when
902 /// the next session creation request is issued.
903 ///
904 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
905 /// with argument "serverInstanceAddressIgnored" on any `ClientListener` listening to the
906 /// related `LightstreamerClient`.
907 ///
908 /// # Parameters
909 ///
910 /// * `server_instance_address_ignored`: `true` or `false`, to ignore or not the server instance
911 /// address sent by the server.
912 ///
913 /// See also `ConnectionDetails.setServerAddress()`
914 pub fn set_server_instance_address_ignored(&mut self, server_instance_address_ignored: bool) {
915 self.server_instance_address_ignored = server_instance_address_ignored;
916 }
917
918 /// Setter method that sets the maximum time allowed for attempts to recover the current session
919 /// upon an interruption, after which a new session will be created. If the given value (expressed
920 /// in milliseconds) equals 0, then any attempt to recover the current session will be prevented
921 /// in the first place.
922 ///
923 /// In fact, in an attempt to recover the current session, the client will periodically try
924 /// to access the Server at the address related with the current session. In some cases, this
925 /// timeout, by enforcing a fresh connection attempt, may prevent an infinite sequence of unsuccessful
926 /// attempts to access the Server.
927 ///
928 /// Note that, when the Server is reached, the recovery may fail due to a Server side timeout
929 /// on the retention of the session and the updates sent. In that case, a new session will
930 /// be created anyway. A setting smaller than the Server timeouts may prevent such useless
931 /// failures, but, if too small, it may also prevent successful recovery in some cases.
932 ///
933 /// 15000 (15 seconds).
934 ///
935 /// This value can be set and changed at any time.
936 ///
937 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
938 /// with argument "sessionRecoveryTimeout" on any `ClientListener` listening to the related
939 /// `LightstreamerClient`.
940 ///
941 /// # Parameters
942 ///
943 /// * `session_recovery_timeout`: The maximum time allowed for recovery attempts, expressed
944 /// in milliseconds, including 0.
945 ///
946 /// # Raises
947 ///
948 /// * `LightstreamerError`: if the value is invalid
949 pub fn set_session_recovery_timeout(
950 &mut self,
951 session_recovery_timeout: u64,
952 ) -> Result<(), LightstreamerError> {
953 if session_recovery_timeout == 0 {
954 self.session_recovery_timeout = session_recovery_timeout;
955 return Ok(());
956 }
957
958 if session_recovery_timeout < self.retry_delay {
959 return Err(LightstreamerError::invalid_argument(
960 "Session recovery timeout should be greater than or equal to retry delay",
961 ));
962 }
963
964 self.session_recovery_timeout = session_recovery_timeout;
965 Ok(())
966 }
967
968 /// Setter method that turns on or off the slowing algorithm. This heuristic algorithm tries
969 /// to detect when the client CPU is not able to keep the pace of the events sent by the Server
970 /// on a streaming connection. In that case, an automatic transition to polling is performed.
971 ///
972 /// In polling, the client handles all the data before issuing the next poll, hence a slow
973 /// client would just delay the polls, while the Server accumulates and merges the events and
974 /// ensures that no obsolete data is sent.
975 ///
976 /// Only in very slow clients, the next polling request may be so much delayed that the Server
977 /// disposes the session first, because of its protection timeouts. In this case, a request
978 /// for a fresh session will be reissued by the client and this may happen in cycle.
979 ///
980 /// false.
981 ///
982 /// This setting should be performed before calling the `LightstreamerClient.connect()` method.
983 /// However, the value can be changed at any time: the supplied value will be used for the
984 /// next streaming connection (either a bind or a brand new session).
985 ///
986 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
987 /// with argument "slowingEnabled" on any `ClientListener` listening to the related `LightstreamerClient`.
988 ///
989 /// # Parameters
990 ///
991 /// * `slowing_enabled`: `true` or `false`, to enable or disable the heuristic algorithm that
992 /// lowers the item update frequency.
993 pub fn set_slowing_enabled(&mut self, slowing_enabled: bool) {
994 self.slowing_enabled = slowing_enabled;
995 }
996
997 /// Setter method that sets the extra time the client is allowed to wait when an expected keepalive
998 /// packet has not been received on a stream connection (and no actual data has arrived), before
999 /// entering the "STALLED" status.
1000 ///
1001 /// 2000 (2 seconds).
1002 ///
1003 /// This value can be set and changed at any time.
1004 ///
1005 /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
1006 /// with argument "stalledTimeout" on any `ClientListener` listening to the related `LightstreamerClient`.
1007 ///
1008 /// # Parameters
1009 ///
1010 /// * `stalled_timeout`: The idle time (in milliseconds) allowed before entering the "STALLED"
1011 /// status.
1012 ///
1013 /// # Raises
1014 ///
1015 /// * `LightstreamerError`: if zero is configured or value violates timeout constraints
1016 ///
1017 /// See also `setReconnectTimeout()`
1018 ///
1019 /// See also `setKeepaliveInterval()`
1020 pub fn set_stalled_timeout(&mut self, stalled_timeout: u64) -> Result<(), LightstreamerError> {
1021 if stalled_timeout == 0 {
1022 return Err(LightstreamerError::invalid_argument(
1023 "Stalled timeout cannot be zero",
1024 ));
1025 }
1026
1027 if stalled_timeout >= self.keepalive_interval {
1028 return Err(LightstreamerError::invalid_argument(
1029 "Stalled timeout should be less than keepalive interval",
1030 ));
1031 }
1032
1033 if stalled_timeout >= self.reconnect_timeout {
1034 return Err(LightstreamerError::invalid_argument(
1035 "Stalled timeout should be less than reconnect timeout",
1036 ));
1037 }
1038
1039 self.stalled_timeout = stalled_timeout;
1040
1041 Ok(())
1042 }
1043
1044 /// Returns whether the client is configured for polling mode.
1045 pub fn is_polling(&self) -> bool {
1046 self.polling
1047 }
1048
1049 /// Setter method that configures the client for polling mode.
1050 ///
1051 /// In polling mode, the client will open polling connections instead of streaming connections.
1052 /// This can be useful in environments where streaming connections are not supported or not
1053 /// recommended.
1054 ///
1055 /// If `polling` is set to `true`, the following settings will be automatically configured:
1056 /// - `polling_interval` will be set to 0 (asynchronous polling)
1057 /// - `idle_timeout` will be set to 19000 (19 seconds)
1058 ///
1059 /// # Parameters
1060 ///
1061 /// * `polling`: `true` to enable polling mode, `false` to disable it.
1062 pub fn set_polling(&mut self, polling: bool) {
1063 self.polling = polling;
1064
1065 if polling {
1066 self.polling_interval = 0;
1067 self.idle_timeout = 19000;
1068 }
1069 }
1070
1071 /// Inquiry method that gets the time-to-live for a request, expressed in milliseconds.
1072 ///
1073 /// If the time-to-live is exceeded and the request is still pending, it will be aborted by
1074 /// the client library.
1075 ///
1076 /// # Returns
1077 ///
1078 /// The time-to-live for a request, expressed in milliseconds. If `None`, the request will
1079 /// be kept until completion.
1080 pub fn get_ttl_millis(&self) -> Option<u64> {
1081 self.ttl_millis
1082 }
1083
1084 /// Setter method that sets the time-to-live for a request, expressed in milliseconds.
1085 ///
1086 /// If the time-to-live is exceeded and the request is still pending, it will be aborted by
1087 /// the client library.
1088 ///
1089 /// # Parameters
1090 ///
1091 /// * `ttl_millis`: The time-to-live for a request, expressed in milliseconds. If `None`, the
1092 /// request will be kept until completion.
1093 pub fn set_ttl_millis(&mut self, ttl_millis: Option<u64>) {
1094 self.ttl_millis = ttl_millis;
1095 }
1096
1097 /// Inquiry method that gets the list of supported "diff" formats accepted for the indication
1098 /// of update values.
1099 ///
1100 /// The protocol allows the Server to choose among a few "diff" algorithms to express new values
1101 /// as differences from previous values. This setting allows the client to restrict the set
1102 /// of accepted formats.
1103 ///
1104 /// # Returns
1105 ///
1106 /// The list of supported "diff" formats, or `None` if all formats are accepted.
1107 pub fn get_supported_diffs(&self) -> Option<&String> {
1108 self.supported_diffs.as_ref()
1109 }
1110
1111 /// Setter method that sets the list of supported "diff" formats accepted for the indication
1112 /// of update values.
1113 ///
1114 /// The protocol allows the Server to choose among a few "diff" algorithms to express new values
1115 /// as differences from previous values. This setting allows the client to restrict the set
1116 /// of accepted formats.
1117 ///
1118 /// # Parameters
1119 ///
1120 /// * `supported_diffs`: The list of supported "diff" formats, or `None` to accept all formats.
1121 /// The list should be a comma-separated string of format tags.
1122 pub fn set_supported_diffs(&mut self, supported_diffs: Option<String>) {
1123 self.supported_diffs = supported_diffs;
1124 }
1125}
1126
1127impl Debug for ConnectionOptions {
1128 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1129 f.debug_struct("ConnectionOptions")
1130 .field("content_length", &self.content_length)
1131 .field("first_retry_max_delay", &self.first_retry_max_delay)
1132 .field("forced_transport", &self.forced_transport)
1133 .field("http_extra_headers", &self.http_extra_headers)
1134 .field(
1135 "http_extra_headers_on_session_creation_only",
1136 &self.http_extra_headers_on_session_creation_only,
1137 )
1138 .field("idle_timeout", &self.idle_timeout)
1139 .field("keepalive_interval", &self.keepalive_interval)
1140 .field("polling_interval", &self.polling_interval)
1141 .field("proxy", &self.proxy)
1142 .field("real_max_bandwidth", &self.real_max_bandwidth)
1143 .field("reconnect_timeout", &self.reconnect_timeout)
1144 .field("requested_max_bandwidth", &self.requested_max_bandwidth)
1145 .field("retry_delay", &self.retry_delay)
1146 .field(
1147 "reverse_heartbeat_interval",
1148 &self.reverse_heartbeat_interval,
1149 )
1150 .field(
1151 "server_instance_address_ignored",
1152 &self.server_instance_address_ignored,
1153 )
1154 .field("session_recovery_timeout", &self.session_recovery_timeout)
1155 .field("slowing_enabled", &self.slowing_enabled)
1156 .field("stalled_timeout", &self.stalled_timeout)
1157 .finish()
1158 }
1159}
1160
1161impl Default for ConnectionOptions {
1162 fn default() -> Self {
1163 Self {
1164 content_length: None,
1165 first_retry_max_delay: 0,
1166 forced_transport: None,
1167 http_extra_headers: None,
1168 http_extra_headers_on_session_creation_only: false,
1169 idle_timeout: 19000,
1170 keepalive_interval: 0,
1171 polling_interval: 0,
1172 proxy: None,
1173 real_max_bandwidth: None,
1174 reconnect_timeout: 3000,
1175 _reduce_head: false,
1176 requested_max_bandwidth: None,
1177 retry_delay: 4000,
1178 reverse_heartbeat_interval: 0,
1179 send_sync: false,
1180 server_instance_address_ignored: false,
1181 session_recovery_timeout: 15000,
1182 slowing_enabled: false,
1183 stalled_timeout: 2000,
1184 polling: false,
1185 ttl_millis: None,
1186 supported_diffs: None,
1187 reconnection_config: ReconnectionConfig::default(),
1188 heartbeat_config: HeartbeatConfig::default(),
1189 auto_reconnect_enabled: false,
1190 }
1191 }
1192}
1193
1194impl ConnectionOptions {
1195 /// Gets the current reconnection configuration.
1196 ///
1197 /// # Returns
1198 ///
1199 /// The current reconnection configuration
1200 pub fn get_reconnection_config(&self) -> &ReconnectionConfig {
1201 &self.reconnection_config
1202 }
1203
1204 /// Sets the reconnection configuration.
1205 ///
1206 /// # Parameters
1207 ///
1208 /// * `config`: The reconnection configuration to set
1209 pub fn set_reconnection_config(&mut self, config: ReconnectionConfig) {
1210 self.reconnection_config = config;
1211 }
1212
1213 /// Gets the current heartbeat configuration.
1214 ///
1215 /// # Returns
1216 ///
1217 /// The current heartbeat configuration
1218 pub fn get_heartbeat_config(&self) -> &HeartbeatConfig {
1219 &self.heartbeat_config
1220 }
1221
1222 /// Sets the heartbeat configuration.
1223 ///
1224 /// # Parameters
1225 ///
1226 /// * `config`: The heartbeat configuration to set
1227 pub fn set_heartbeat_config(&mut self, config: HeartbeatConfig) {
1228 self.heartbeat_config = config;
1229 }
1230
1231 /// Gets whether automatic reconnection is enabled.
1232 ///
1233 /// # Returns
1234 ///
1235 /// `true` if automatic reconnection is enabled, `false` otherwise
1236 pub fn is_auto_reconnect_enabled(&self) -> bool {
1237 self.auto_reconnect_enabled
1238 }
1239
1240 /// Enables or disables automatic reconnection.
1241 ///
1242 /// # Parameters
1243 ///
1244 /// * `enabled`: Whether to enable automatic reconnection
1245 pub fn set_auto_reconnect_enabled(&mut self, enabled: bool) {
1246 self.auto_reconnect_enabled = enabled;
1247 }
1248}
1249
1250#[cfg(test)]
1251mod tests {
1252 use super::*;
1253
1254 #[test]
1255 fn test_new_connection_options() {
1256 let options = ConnectionOptions::new();
1257
1258 // Verify default values
1259 assert_eq!(options.get_content_length(), None);
1260 assert_eq!(options.get_first_retry_max_delay(), 100);
1261 assert_eq!(options.get_forced_transport(), None);
1262 assert_eq!(options.get_http_extra_headers(), None);
1263 assert!(!options.is_http_extra_headers_on_session_creation_only());
1264 assert_eq!(options.get_idle_timeout(), 19000);
1265 assert_eq!(options.get_keepalive_interval(), 0);
1266 assert_eq!(options.get_polling_interval(), 0);
1267 assert_eq!(options.get_reconnect_timeout(), 3000);
1268 assert_eq!(options.get_retry_delay(), 4000);
1269 assert_eq!(options.get_reverse_heartbeat_interval(), 0);
1270 assert!(!options.is_server_instance_address_ignored());
1271 assert_eq!(options.get_session_recovery_timeout(), 15000);
1272 assert!(!options.is_slowing_enabled());
1273 assert_eq!(options.get_stalled_timeout(), 2000);
1274 assert!(options.get_send_sync());
1275 }
1276
1277 #[test]
1278 fn test_set_content_length() {
1279 let mut options = ConnectionOptions::new();
1280
1281 // Test valid content length
1282 assert!(options.set_content_length(10000).is_ok());
1283 assert_eq!(options.get_content_length(), Some(10000));
1284
1285 // Test invalid (zero) content length
1286 assert!(options.set_content_length(0).is_err());
1287 }
1288
1289 #[test]
1290 fn test_set_first_retry_max_delay() {
1291 let mut options = ConnectionOptions::new();
1292
1293 // Test valid delay
1294 assert!(options.set_first_retry_max_delay(500).is_ok());
1295 assert_eq!(options.get_first_retry_max_delay(), 500);
1296
1297 // Test invalid (zero) delay
1298 assert!(options.set_first_retry_max_delay(0).is_err());
1299 }
1300
1301 #[test]
1302 fn test_set_forced_transport() {
1303 let mut options = ConnectionOptions::new();
1304
1305 // Test setting different transport types
1306 options.set_forced_transport(Some(Transport::WsStreaming));
1307 assert_eq!(
1308 options.get_forced_transport(),
1309 Some(&Transport::WsStreaming)
1310 );
1311
1312 options.set_forced_transport(Some(Transport::HttpStreaming));
1313 assert_eq!(
1314 options.get_forced_transport(),
1315 Some(&Transport::HttpStreaming)
1316 );
1317
1318 options.set_forced_transport(Some(Transport::WsPolling));
1319 assert_eq!(options.get_forced_transport(), Some(&Transport::WsPolling));
1320
1321 options.set_forced_transport(Some(Transport::HttpPolling));
1322 assert_eq!(
1323 options.get_forced_transport(),
1324 Some(&Transport::HttpPolling)
1325 );
1326
1327 options.set_forced_transport(Some(Transport::Ws));
1328 assert_eq!(options.get_forced_transport(), Some(&Transport::Ws));
1329
1330 options.set_forced_transport(Some(Transport::Http));
1331 assert_eq!(options.get_forced_transport(), Some(&Transport::Http));
1332
1333 // Test setting None
1334 options.set_forced_transport(None);
1335 assert_eq!(options.get_forced_transport(), None);
1336 }
1337
1338 #[test]
1339 fn test_set_http_extra_headers() {
1340 let mut options = ConnectionOptions::new();
1341
1342 // Test setting headers
1343 let mut headers = HashMap::new();
1344 headers.insert("X-Custom-Header".to_string(), "Value".to_string());
1345 headers.insert("X-Another-Header".to_string(), "AnotherValue".to_string());
1346
1347 options.set_http_extra_headers(Some(headers.clone()));
1348 assert_eq!(options.get_http_extra_headers(), Some(&headers));
1349
1350 // Test setting None
1351 options.set_http_extra_headers(None);
1352 assert_eq!(options.get_http_extra_headers(), None);
1353 }
1354
1355 #[test]
1356 fn test_set_http_extra_headers_on_session_creation_only() {
1357 let mut options = ConnectionOptions::new();
1358
1359 // Test setting to true
1360 options.set_http_extra_headers_on_session_creation_only(true);
1361 assert!(options.is_http_extra_headers_on_session_creation_only());
1362
1363 // Test setting to false
1364 options.set_http_extra_headers_on_session_creation_only(false);
1365 assert!(!options.is_http_extra_headers_on_session_creation_only());
1366 }
1367
1368 #[test]
1369 fn test_set_idle_timeout() {
1370 let mut options = ConnectionOptions::new();
1371
1372 // Test valid timeout
1373 assert!(options.set_idle_timeout(15000).is_ok());
1374 assert_eq!(options.get_idle_timeout(), 15000);
1375
1376 // Test invalid (zero) timeout
1377 assert!(options.set_idle_timeout(0).is_err());
1378 }
1379
1380 #[test]
1381 fn test_set_keepalive_interval() {
1382 let mut options = ConnectionOptions::new();
1383
1384 // Test valid interval
1385 assert!(options.set_keepalive_interval(5000).is_ok());
1386 assert_eq!(options.get_keepalive_interval(), 5000);
1387
1388 // Test zero interval (special case - valid)
1389 assert!(options.set_keepalive_interval(0).is_ok());
1390 assert_eq!(options.get_keepalive_interval(), 0);
1391 }
1392
1393 #[test]
1394 fn test_set_polling_interval() {
1395 let mut options = ConnectionOptions::new();
1396
1397 assert!(options.set_idle_timeout(2000).is_ok());
1398 assert!(options.set_polling_interval(3000).is_ok());
1399 assert_eq!(options.get_polling_interval(), 3000);
1400 assert!(options.set_polling_interval(0).is_ok());
1401 assert_eq!(options.get_polling_interval(), 0);
1402 assert!(options.set_idle_timeout(19000).is_ok());
1403 assert!(options.set_polling_interval(10000).is_err());
1404 }
1405
1406 #[test]
1407 fn test_set_reconnect_timeout() {
1408 let mut options = ConnectionOptions::new();
1409
1410 // Test valid timeout
1411 assert!(options.set_reconnect_timeout(5000).is_ok());
1412 assert_eq!(options.get_reconnect_timeout(), 5000);
1413
1414 // Test invalid (zero) timeout
1415 assert!(options.set_reconnect_timeout(0).is_err());
1416 }
1417
1418 #[test]
1419 fn test_set_requested_max_bandwidth() {
1420 let mut options = ConnectionOptions::new();
1421
1422 // Test valid bandwidth
1423 assert!(options.set_requested_max_bandwidth(Some(10.5)).is_ok());
1424 assert_eq!(options.get_requested_max_bandwidth(), Some(10.5));
1425
1426 // Test invalid (zero) bandwidth
1427 assert!(options.set_requested_max_bandwidth(Some(0.0)).is_err());
1428
1429 // Test setting None
1430 assert!(options.set_requested_max_bandwidth(None).is_ok());
1431 assert_eq!(options.get_requested_max_bandwidth(), None);
1432 }
1433
1434 #[test]
1435 fn test_set_retry_delay() {
1436 let mut options = ConnectionOptions::new();
1437
1438 // Test valid delay
1439 assert!(options.set_retry_delay(3000).is_ok());
1440 assert_eq!(options.get_retry_delay(), 3000);
1441
1442 // Test invalid (zero) delay
1443 assert!(options.set_retry_delay(0).is_err());
1444 }
1445
1446 #[test]
1447 fn test_set_reverse_heartbeat_interval() {
1448 let mut options = ConnectionOptions::new();
1449
1450 // Test valid interval
1451 assert!(options.set_reverse_heartbeat_interval(5000).is_ok());
1452 assert_eq!(options.get_reverse_heartbeat_interval(), 5000);
1453
1454 // Test zero interval (special case - valid)
1455 assert!(options.set_reverse_heartbeat_interval(0).is_ok());
1456 assert_eq!(options.get_reverse_heartbeat_interval(), 0);
1457 }
1458
1459 #[test]
1460 fn test_set_server_instance_address_ignored() {
1461 let mut options = ConnectionOptions::new();
1462
1463 // Test setting to true
1464 options.set_server_instance_address_ignored(true);
1465 assert!(options.is_server_instance_address_ignored());
1466
1467 // Test setting to false
1468 options.set_server_instance_address_ignored(false);
1469 assert!(!options.is_server_instance_address_ignored());
1470 }
1471
1472 #[test]
1473 fn test_set_session_recovery_timeout() {
1474 let mut options = ConnectionOptions::new();
1475
1476 // Test valid timeout
1477 assert!(options.set_session_recovery_timeout(10000).is_ok());
1478 assert_eq!(options.get_session_recovery_timeout(), 10000);
1479
1480 // Test zero timeout (special case - valid)
1481 assert!(options.set_session_recovery_timeout(0).is_ok());
1482 assert_eq!(options.get_session_recovery_timeout(), 0);
1483 }
1484
1485 #[test]
1486 fn test_set_slowing_enabled() {
1487 let mut options = ConnectionOptions::new();
1488
1489 // Test setting to true
1490 options.set_slowing_enabled(true);
1491 assert!(options.is_slowing_enabled());
1492
1493 // Test setting to false
1494 options.set_slowing_enabled(false);
1495 assert!(!options.is_slowing_enabled());
1496 }
1497
1498 #[test]
1499 fn test_set_stalled_timeout() {
1500 let mut options = ConnectionOptions::new();
1501
1502 assert!(options.set_keepalive_interval(5000).is_ok());
1503 assert!(options.set_stalled_timeout(1000).is_ok());
1504 assert_eq!(options.get_stalled_timeout(), 1000);
1505 assert!(options.set_stalled_timeout(0).is_err());
1506 assert!(options.set_stalled_timeout(6000).is_err());
1507
1508 assert!(options.set_reconnect_timeout(2000).is_ok());
1509 assert!(options.set_stalled_timeout(1500).is_ok());
1510 assert!(options.set_stalled_timeout(2500).is_err());
1511 }
1512
1513 #[test]
1514 fn test_set_polling() {
1515 let mut options = ConnectionOptions::new();
1516
1517 // Test setting to true
1518 options.set_polling(true);
1519 assert!(options.is_polling());
1520 assert_eq!(options.get_polling_interval(), 0); // Should be set to 0
1521 assert_eq!(options.get_idle_timeout(), 19000); // Should be set to 19000
1522
1523 // Test setting to false
1524 options.set_polling(false);
1525 assert!(!options.is_polling());
1526 }
1527
1528 #[test]
1529 fn test_set_ttl_millis() {
1530 let mut options = ConnectionOptions::new();
1531
1532 // Test setting value
1533 options.set_ttl_millis(Some(5000));
1534 assert_eq!(options.get_ttl_millis(), Some(5000));
1535
1536 // Test setting None
1537 options.set_ttl_millis(None);
1538 assert_eq!(options.get_ttl_millis(), None);
1539 }
1540
1541 #[test]
1542 fn test_set_supported_diffs() {
1543 let mut options = ConnectionOptions::new();
1544
1545 // Test setting value
1546 options.set_supported_diffs(Some("TLCP-diff,JSON-patch".to_string()));
1547 if let Some(diffs) = options.get_supported_diffs() {
1548 assert_eq!(diffs, "TLCP-diff,JSON-patch");
1549 }
1550
1551 // Test setting None
1552 options.set_supported_diffs(None);
1553 assert_eq!(options.get_supported_diffs(), None);
1554 }
1555
1556 #[test]
1557 fn test_debug_implementation() {
1558 let options = ConnectionOptions::new();
1559
1560 // Test that Debug implementation works without panicking
1561 let debug_string = format!("{:?}", options);
1562
1563 // Verify it contains some expected fields
1564 assert!(debug_string.contains("content_length"));
1565 assert!(debug_string.contains("first_retry_max_delay"));
1566 assert!(debug_string.contains("forced_transport"));
1567 assert!(debug_string.contains("http_extra_headers"));
1568 }
1569
1570 #[test]
1571 fn test_combined_settings() {
1572 let mut options = ConnectionOptions::new();
1573
1574 // Test multiple settings together
1575 assert!(options.set_keepalive_interval(5000).is_ok());
1576 assert!(options.set_stalled_timeout(2000).is_ok());
1577 assert!(options.set_reconnect_timeout(3000).is_ok());
1578 assert!(options.set_first_retry_max_delay(100).is_ok());
1579 assert!(options.set_retry_delay(4000).is_ok());
1580
1581 // Verify all settings were applied correctly
1582 assert_eq!(options.get_keepalive_interval(), 5000);
1583 assert_eq!(options.get_stalled_timeout(), 2000);
1584 assert_eq!(options.get_reconnect_timeout(), 3000);
1585 assert_eq!(options.get_first_retry_max_delay(), 100);
1586 assert_eq!(options.get_retry_delay(), 4000);
1587 }
1588}