Skip to main content

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}