lightstreamer_client/
connection_details.rs

1use crate::client_listener::ClientListener;
2use crate::error::IllegalArgumentException;
3
4use std::error::Error;
5use std::fmt::{self, Debug, Formatter};
6
7/// Used by `LightstreamerClient` to provide a basic connection properties data object.
8///
9/// Data object that contains the configuration settings needed to connect to a Lightstreamer Server.
10///
11/// An instance of this class is attached to every `LightstreamerClient` as `LightstreamerClient.connectionDetails`
12///
13/// See also `LightstreamerClient`
14pub struct ConnectionDetails {
15    adapter_set: Option<String>,
16    client_ip: Option<String>,
17    server_address: Option<String>,
18    server_instance_address: Option<String>,
19    server_socket_name: Option<String>,
20    session_id: Option<String>,
21    user: Option<String>,
22    password: Option<String>,
23    listeners: Vec<Box<dyn ClientListener>>,
24}
25
26impl ConnectionDetails {
27    /// Inquiry method that gets the name of the Adapter Set (which defines the Metadata Adapter
28    /// and one or several Data Adapters) mounted on Lightstreamer Server that supply all the
29    /// items used in this application.
30    ///
31    /// # Returns
32    ///
33    /// The name of the Adapter Set; returns `None` if no name has been configured, that means
34    /// that the "DEFAULT" Adapter Set is used.
35    ///
36    /// See also `setAdapterSet()`
37    pub fn get_adapter_set(&self) -> Option<&String> {
38        self.adapter_set.as_ref()
39    }
40
41    /// Inquiry method that gets the IP address of this client as seen by the Server which is
42    /// serving the current session as the client remote address (note that it may not correspond
43    /// to the client host; for instance it may refer to an intermediate proxy). If, upon a new
44    /// session, this address changes, it may be a hint that the intermediary network nodes handling
45    /// the connection have changed, hence the network capabilities may be different. The library
46    /// uses this information to optimize the connection.
47    ///
48    /// Note that in case of polling or in case rebind requests are needed, subsequent requests
49    /// related to the same session may, in principle, expose a different IP address to the Server;
50    /// these changes would not be reported.
51    ///
52    /// If a session is not currently active, `None` is returned; soon after a session is established,
53    /// the value may become available; but it is possible that this information is not provided
54    /// by the Server and that it will never be available.
55    ///
56    /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
57    /// with argument "clientIp" on any `ClientListener` listening to the related `LightstreamerClient`.
58    ///
59    /// # Returns
60    ///
61    /// A canonical representation of an IP address (it can be either IPv4 or IPv6), or `None`.
62    pub fn get_client_ip(&self) -> Option<&String> {
63        self.client_ip.as_ref()
64    }
65
66    /// Retrieves a reference to the password, if set.
67    ///
68    /// This method is crucial for accessing sensitive information in a controlled manner. It returns
69    /// an immutable reference to the password, encapsulated within an `Option`. The use of `Option`
70    /// signifies that the password may or may not be present, thus providing flexibility in scenarios
71    /// where a password is optional. By returning a reference, we avoid unnecessary cloning of the
72    /// password data, which could have security implications and also incur a performance cost.
73    ///
74    /// # Returns
75    /// An `Option` containing a reference to the password `String` if it exists, or `None` if the
76    /// password has not been set. This allows calling code to handle the presence or absence of a
77    /// password appropriately without risking exposure of the password itself.
78    pub fn get_password(&self) -> Option<&String> {
79        self.password.as_ref()
80    }
81
82    /// Inquiry method that gets the configured address of Lightstreamer Server.
83    ///
84    /// # Returns
85    ///
86    /// The configured address of Lightstreamer Server.
87    pub fn get_server_address(&self) -> Option<&String> {
88        self.server_address.as_ref()
89    }
90
91    /// Inquiry method that gets the server address to be used to issue all requests related to
92    /// the current session. In fact, when a Server cluster is in place, the Server address specified
93    /// through `setServerAddress()` can identify various Server instances; in order to ensure that
94    /// all requests related to a session are issued to the same Server instance, the Server can
95    /// answer to the session opening request by providing an address which uniquely identifies
96    /// its own instance. When this is the case, this address is returned by the method; otherwise,
97    /// `None` is returned.
98    ///
99    /// Note that the addresses will always have the `http:` or `https:` scheme. In case WebSockets
100    /// are used, the specified scheme is internally converted to match the related WebSocket protocol
101    /// (i.e. `http` becomes `ws` while `https` becomes `wss`).
102    ///
103    /// Server Clustering is an optional feature, available depending on Edition and License Type.
104    /// To know what features are enabled by your license, please see the License tab of the Monitoring
105    /// Dashboard (by default, available at /dashboard).
106    ///
107    /// The method gives a meaningful answer only when a session is currently active.
108    ///
109    /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
110    /// with argument "serverInstanceAddress" on any `ClientListener` listening to the related
111    /// `LightstreamerClient`.
112    ///
113    /// # Returns
114    ///
115    /// Address used to issue all requests related to the current session.
116    pub fn get_server_instance_address(&self) -> Option<&String> {
117        self.server_instance_address.as_ref()
118    }
119
120    /// Inquiry method that gets the instance name of the Server which is serving the current session.
121    /// To be more precise, each answering port configured on a Server instance (through a `<http_server>`
122    /// or `<https_server>` element in the Server configuration file) can be given a different name;
123    /// the name related to the port to which the session opening request has been issued is returned.
124    ///
125    /// Note that each rebind to the same session can, potentially, reach the Server on a port different
126    /// than the one used for the previous request, depending on the behavior of intermediate nodes.
127    /// However, the only meaningful case is when a Server cluster is in place and it is configured
128    /// in such a way that the port used for all `bind_session` requests differs from the port used
129    /// for the initial `create_session` request.
130    ///
131    /// Server Clustering is an optional feature, available depending on Edition and License Type.
132    /// To know what features are enabled by your license, please see the License tab of the Monitoring
133    /// Dashboard (by default, available at /dashboard).
134    ///
135    /// If a session is not currently active, `None` is returned; soon after a session is established,
136    /// the value will become available.
137    ///
138    /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
139    /// with argument "serverSocketName" on any `ClientListener` listening to the related `LightstreamerClient`.
140    ///
141    /// # Returns
142    ///
143    /// Name configured for the Server instance which is managing the current session, or `None`.
144    pub fn get_server_socket_name(&self) -> Option<&String> {
145        self.server_socket_name.as_ref()
146    }
147
148    /// Inquiry method that gets the ID associated by the server to this client session.
149    ///
150    /// The method gives a meaningful answer only when a session is currently active.
151    ///
152    /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
153    /// with argument "sessionId" on any `ClientListener` listening to the related `LightstreamerClient`.
154    ///
155    /// # Returns
156    ///
157    /// ID assigned by the Server to this client session.
158    pub fn get_session_id(&self) -> Option<&String> {
159        self.session_id.as_ref()
160    }
161
162    /// Inquiry method that gets the username to be used for the authentication on Lightstreamer
163    /// Server when initiating the session.
164    ///
165    /// # Returns
166    ///
167    /// The username to be used for the authentication on Lightstreamer Server; returns `None`
168    /// if no user name has been configured.
169    pub fn get_user(&self) -> Option<&String> {
170        self.user.as_ref()
171    }
172
173    /// Creates a new ConnectionDetails object with default values.
174    pub fn new(
175        server_address: Option<&str>,
176        adapter_set: Option<&str>,
177        user: Option<&str>,
178        password: Option<&str>,
179    ) -> Result<ConnectionDetails, Box<dyn Error>> {
180        let mut connection_details = ConnectionDetails::default();
181        connection_details.set_server_address(server_address.map(|s| s.to_string()))?;
182        connection_details.set_adapter_set(adapter_set.map(|s| s.to_string()));
183        connection_details.set_user(user.map(|s| s.to_string()));
184        connection_details.set_password(password.map(|s| s.to_string()));
185
186        Ok(connection_details)
187    }
188
189    /// Setter method that sets the name of the Adapter Set mounted on Lightstreamer Server to
190    /// be used to handle all requests in the session.
191    ///
192    /// An Adapter Set defines the Metadata Adapter and one or several Data Adapters. It is configured
193    /// on the server side through an "adapters.xml" file; the name is configured through the "id"
194    /// attribute in the `<adapters_conf>` element.
195    ///
196    /// The default Adapter Set, configured as "DEFAULT" on the Server.
197    ///
198    /// The Adapter Set name should be set on the `LightstreamerClient.connectionDetails` object
199    /// before calling the `LightstreamerClient.connect()` method. However, the value can be changed
200    /// at any time: the supplied value will be used for the next time a new session is requested
201    /// to the server.
202    ///
203    /// This setting can also be specified in the `LightstreamerClient` constructor.
204    ///
205    /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
206    /// with argument "adapterSet" on any `ClientListener` listening to the related `LightstreamerClient`.
207    ///
208    /// # Parameters
209    ///
210    /// * `adapter_set`: The name of the Adapter Set to be used. A `None` value is equivalent to
211    ///   the "DEFAULT" name.
212    pub fn set_adapter_set(&mut self, adapter_set: Option<String>) {
213        self.adapter_set = Some(adapter_set.unwrap_or("DEFAULT".to_string()));
214
215        // Notify listeners about the property change
216        for listener in &self.listeners {
217            listener.on_property_change("adapterSet");
218        }
219    }
220
221    /// Setter method that sets the password to be used for the authentication on Lightstreamer
222    /// Server when initiating the session. The Metadata Adapter is responsible for checking the
223    /// credentials (username and password).
224    ///
225    /// If no password is supplied, no password information will be sent at session initiation.
226    /// The Metadata Adapter, however, may still allow the session.
227    ///
228    /// The password should be set on the `LightstreamerClient.connectionDetails` object before
229    /// calling the `LightstreamerClient.connect()` method. However, the value can be changed at
230    /// any time: the supplied value will be used for the next time a new session is requested to
231    /// the server.
232    ///
233    /// NOTE: The password string will be stored in the current instance. That is necessary in order
234    /// to allow automatic reconnection/reauthentication for fail-over. For maximum security, avoid
235    /// using an actual private password to authenticate on Lightstreamer Server; rather use a session-id
236    /// originated by your web/application server, that can be checked by your Metadata Adapter.
237    ///
238    /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
239    /// with argument "password" on any `ClientListener` listening to the related `LightstreamerClient`.
240    ///
241    /// # Parameters
242    ///
243    /// * `password`: The password to be used for the authentication on Lightstreamer Server. The
244    ///   password can be `None`.
245    ///
246    /// See also `setUser()`
247    pub fn set_password(&mut self, password: Option<String>) {
248        self.password = password;
249
250        // Notify listeners about the property change
251        for listener in &self.listeners {
252            listener.on_property_change("password");
253        }
254    }
255
256    /// Setter method that sets the address of Lightstreamer Server.
257    ///
258    /// Note that the addresses specified must always have the `http:` or `https:` scheme. In case
259    /// WebSockets are used, the specified scheme is internally converted to match the related WebSocket
260    /// protocol (i.e. `http` becomes `ws` while `https` becomes `wss`).
261    ///
262    /// WSS/HTTPS is an optional feature, available depending on Edition and License Type. To know
263    /// what features are enabled by your license, please see the License tab of the Monitoring
264    /// Dashboard (by default, available at /dashboard).
265    ///
266    /// If no server address is supplied the client will be unable to connect.
267    ///
268    /// This method can be called at any time. If called while connected, it will be applied when
269    /// the next session creation request is issued. This setting can also be specified in the
270    /// `LightstreamerClient` constructor.
271    ///
272    /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
273    /// with argument "serverAddress" on any `ClientListener` listening to the related `LightstreamerClient`.
274    ///
275    /// # Parameters
276    ///
277    /// * `server_address`: The full address of Lightstreamer Server. A `None` value can also be
278    ///   used, to restore the default value.
279    ///
280    /// An IPv4 or IPv6 can also be used in place of a hostname. Some examples of valid values include:
281    ///
282    /// - `http://push.mycompany.com`
283    /// - `http://push.mycompany.com:8080`
284    /// - `http://79.125.7.252`
285    /// - `http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]`
286    /// - `http://[2001:0db8:85a3::8a2e:0370:7334]:8080`
287    ///
288    /// # Raises
289    ///
290    /// * `IllegalArgumentException`: if the given address is not valid.
291    pub fn set_server_address(
292        &mut self,
293        server_address: Option<String>,
294    ) -> Result<(), IllegalArgumentException> {
295        // Validate the server address
296        if let Some(address) = &server_address {
297            if !address.starts_with("http://") && !address.starts_with("https://") {
298                return Err(IllegalArgumentException::new(
299                    "Invalid server address: must start with http:// or https://",
300                ));
301            }
302        }
303
304        self.server_address = server_address;
305
306        // Notify listeners about the property change
307        for listener in &self.listeners {
308            listener.on_property_change("serverAddress");
309        }
310
311        Ok(())
312    }
313
314    /// Setter method that sets the username to be used for the authentication on Lightstreamer
315    /// Server when initiating the session. The Metadata Adapter is responsible for checking the
316    /// credentials (username and password).
317    ///
318    /// If no username is supplied, no user information will be sent at session initiation. The
319    /// Metadata Adapter, however, may still allow the session.
320    ///
321    /// The username should be set on the `LightstreamerClient.connectionDetails` object before
322    /// calling the `LightstreamerClient.connect()` method. However, the value can be changed at
323    /// any time: the supplied value will be used for the next time a new session is requested to
324    /// the server.
325    ///
326    /// A change to this setting will be notified through a call to `ClientListener.onPropertyChange()`
327    /// with argument "user" on any `ClientListener` listening to the related `LightstreamerClient`.
328    ///
329    /// # Parameters
330    ///
331    /// * `user`: The username to be used for the authentication on Lightstreamer Server. The username
332    ///   can be `None`.
333    ///
334    /// See also `setPassword()`
335    pub fn set_user(&mut self, user: Option<String>) {
336        self.user = user;
337
338        // Notify listeners about the property change
339        for listener in &self.listeners {
340            listener.on_property_change("user");
341        }
342    }
343
344    /// Adds a listener that will receive events related to changes in the `ConnectionDetails`.
345    ///
346    /// The same listener can be added to multiple instances of `ConnectionDetails`.
347    ///
348    /// # Parameters
349    ///
350    /// * `listener`: An object that will receive the events as documented in the `ClientListener`
351    ///   interface.
352    pub fn add_listener(&mut self, listener: Box<dyn ClientListener>) {
353        self.listeners.push(listener);
354    }
355
356    /// Removes a listener from the `ConnectionDetails` instance so that it will not receive events
357    /// anymore.
358    ///
359    /// # Parameters
360    ///
361    /// * `listener`: The listener to be removed.
362    pub fn remove_listener(&mut self, _listener: Box<dyn ClientListener>) {
363        unimplemented!("Implement mechanism to remove listener from ConnectionDetails.");
364        //self.listeners.remove(&listener);
365    }
366}
367
368impl Debug for ConnectionDetails {
369    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
370        f.debug_struct("ConnectionDetails")
371            .field("adapter_set", &self.adapter_set)
372            .field("client_ip", &self.client_ip)
373            .field("server_address", &self.server_address)
374            .field("server_instance_address", &self.server_instance_address)
375            .field("server_socket_name", &self.server_socket_name)
376            .field("session_id", &self.session_id)
377            .field("user", &self.user)
378            .field("password", &self.password)
379            .finish()
380    }
381}
382
383impl Default for ConnectionDetails {
384    fn default() -> Self {
385        ConnectionDetails {
386            adapter_set: None,
387            client_ip: None,
388            server_address: None,
389            server_instance_address: None,
390            server_socket_name: None,
391            session_id: None,
392            user: None,
393            password: None,
394            listeners: Vec::new(),
395        }
396    }
397}