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}