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