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}