diff options
author | 2024-03-28 19:19:46 +0100 | |
---|---|---|
committer | 2024-03-28 19:19:46 +0100 | |
commit | e1c0e90581b7ce97f87ddf43267ceb32e28447e3 (patch) | |
tree | a347bfed5552256fcbe5d54798814a1a1d24cf0a | |
parent | b4e12fd1165b5e3960a1294dadec45eb40893b37 (diff) |
✨ (client_listener.rs): Implement Debug trait for ClientListener for better logging
♻️ (connection_details.rs): Refactor ConnectionDetails to use Option for optional fields
♻️ (connection_details.rs): Change new constructor to accept &str and convert to String
✨ (connection_details.rs): Implement Debug trait for ConnectionDetails
♻️ (connection_options.rs): Implement Debug trait for ConnectionOptions
♻️ (lightstreamer_client.rs): Refactor LightstreamerClient to use Option for server_address and adapter_set
✨ (lightstreamer_client.rs): Implement Debug trait for LightstreamerClient
♻️ (main.rs): Update subscribe_to_channel function to use new control.txt URL and parameters
♻️ (main.rs): Update main function to use Option<&str> when creating LightstreamerClient
✨ (proxy.rs): Add Proxy struct and ProxyType enum to handle proxy configurations
-rw-r--r-- | src/client_listener.rs | 4 | ||||
-rw-r--r-- | src/connection_details.rs | 42 | ||||
-rw-r--r-- | src/connection_options.rs | 27 | ||||
-rw-r--r-- | src/lightstreamer_client.rs | 66 | ||||
-rw-r--r-- | src/main.rs | 30 | ||||
-rw-r--r-- | src/proxy.rs | 80 |
6 files changed, 220 insertions, 29 deletions
diff --git a/src/client_listener.rs b/src/client_listener.rs index 06902aa..9d3d6d5 100644 --- a/src/client_listener.rs +++ b/src/client_listener.rs @@ -1,3 +1,5 @@ +use std::fmt::Debug; + /// Interface to be implemented to listen to `LightstreamerClient` events comprehending notifications /// of connection activity and errors. /// @@ -6,7 +8,7 @@ /// has changed. On the other hand, all the notifications for a single `LightstreamerClient`, /// including notifications to `ClientListener`, `SubscriptionListener` and `ClientMessageListener` /// will be dispatched by the same thread. -pub trait ClientListener { +pub trait ClientListener: Debug { /// Event handler that receives a notification when the `ClientListener` instance is removed /// from a `LightstreamerClient` through `LightstreamerClient.removeListener()`. This is the /// last event to be fired on the listener. diff --git a/src/connection_details.rs b/src/connection_details.rs index 1d18bf6..06b6d15 100644 --- a/src/connection_details.rs +++ b/src/connection_details.rs @@ -1,6 +1,10 @@ +use hyper::server; + use crate::client_listener::ClientListener; use crate::IllegalArgumentException; +use std::fmt::{self, Debug, Formatter}; + /// Used by `LightstreamerClient` to provide a basic connection properties data object. /// /// Data object that contains the configuration settings needed to connect to a Lightstreamer Server. @@ -9,9 +13,9 @@ use crate::IllegalArgumentException; /// /// See also `LightstreamerClient` pub struct ConnectionDetails { - adapter_set: String, + adapter_set: Option<String>, client_ip: Option<String>, - server_address: String, + server_address: Option<String>, server_instance_address: Option<String>, server_socket_name: Option<String>, session_id: Option<String>, @@ -31,7 +35,7 @@ impl ConnectionDetails { /// that the "DEFAULT" Adapter Set is used. /// /// See also `setAdapterSet()` - pub fn get_adapter_set(&self) -> &String { + pub fn get_adapter_set(&self) -> Option<&String> { self.adapter_set.as_ref() } @@ -152,18 +156,11 @@ impl ConnectionDetails { } /// Creates a new ConnectionDetails object with default values. - pub fn new(server_address: Option<String>, adapter_set: Option<String>) -> ConnectionDetails { - if let Some(server_address) = server_address { - ConnectionDetails { - server_address: Some(server_address), - adapter_set, - ..Default::default() - } - } else { - ConnectionDetails { - adapter_set, - ..Default::default() - } + pub fn new(server_address: Option<&str>, adapter_set: Option<&str>) -> ConnectionDetails { + ConnectionDetails { + server_address: server_address.map(|s| s.to_string()), // convert &str to String + adapter_set: adapter_set.map(|s| s.to_string()), // convert &str to String + ..Default::default() } } @@ -346,6 +343,21 @@ impl ConnectionDetails { } } +impl Debug for ConnectionDetails { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ConnectionDetails") + .field("adapter_set", &self.adapter_set) + .field("client_ip", &self.client_ip) + .field("server_address", &self.server_address) + .field("server_instance_address", &self.server_instance_address) + .field("server_socket_name", &self.server_socket_name) + .field("session_id", &self.session_id) + .field("user", &self.user) + .field("password", &self.password) + .finish() + } +} + impl Default for ConnectionDetails { fn default() -> Self { ConnectionDetails { diff --git a/src/connection_options.rs b/src/connection_options.rs index aefb0c0..cbe45b5 100644 --- a/src/connection_options.rs +++ b/src/connection_options.rs @@ -1,5 +1,7 @@ use crate::proxy::Proxy; +use std::fmt::{self, Debug, Formatter}; + /// Used by LightstreamerClient to provide an extra connection properties data object. /// Data struct that contains the policy settings used to connect to a Lightstreamer Server. /// An instance of this struct is attached to every LightstreamerClient as connection_options. @@ -121,6 +123,31 @@ impl ConnectionOptions { } } +impl Debug for ConnectionOptions { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ConnectionOptions") + .field("content_length", &self.content_length) + .field("first_retry_max_delay", &self.first_retry_max_delay) + .field("forced_transport", &self.forced_transport) + .field("http_extra_headers", &self.http_extra_headers) + .field("http_extra_headers_on_session_creation_only", &self.http_extra_headers_on_session_creation_only) + .field("idle_timeout", &self.idle_timeout) + .field("keepalive_interval", &self.keepalive_interval) + .field("polling_interval", &self.polling_interval) + .field("proxy", &self.proxy) + .field("real_max_bandwidth", &self.real_max_bandwidth) + .field("reconnect_timeout", &self.reconnect_timeout) + .field("requested_max_bandwidth", &self.requested_max_bandwidth) + .field("retry_delay", &self.retry_delay) + .field("reverse_heartbeat_interval", &self.reverse_heartbeat_interval) + .field("server_instance_address_ignored", &self.server_instance_address_ignored) + .field("session_recovery_timeout", &self.session_recovery_timeout) + .field("slowing_enabled", &self.slowing_enabled) + .field("stalled_timeout", &self.stalled_timeout) + .finish() + } +} + impl Default for ConnectionOptions { fn default() -> Self { ConnectionOptions { diff --git a/src/lightstreamer_client.rs b/src/lightstreamer_client.rs index f91e843..214a722 100644 --- a/src/lightstreamer_client.rs +++ b/src/lightstreamer_client.rs @@ -6,6 +6,7 @@ use crate::subscription::Subscription; use crate::IllegalStateException; use cookie::Cookie; +use std::fmt::{self, Debug, Formatter}; /// Facade class for the management of the communication to Lightstreamer Server. Used to provide /// configuration settings, event handlers, operations for the control of the connection lifecycle, @@ -45,8 +46,8 @@ use cookie::Cookie; /// * `IllegalArgumentException`: if a not valid address is passed. See `ConnectionDetails.setServerAddress()` /// for details. pub struct LightstreamerClient { - server_address: String, - adapter_set: String, + server_address: Option<String>, + adapter_set: Option<String>, /// Data object that contains the details needed to open a connection to a Lightstreamer Server. /// This instance is set up by the `LightstreamerClient` object at its own creation. Properties /// of this object can be overwritten by values received from a Lightstreamer Server. @@ -59,6 +60,19 @@ pub struct LightstreamerClient { subscriptions: Vec<Subscription>, } +impl Debug for LightstreamerClient { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("LightstreamerClient") + .field("server_address", &self.server_address) + .field("adapter_set", &self.adapter_set) + .field("connection_details", &self.connection_details) + .field("connection_options", &self.connection_options) + .field("listeners", &self.listeners) + .field("subscriptions", &self.subscriptions) + .finish() + } +} + impl LightstreamerClient { /// A constant string representing the name of the library. pub const LIB_NAME: &'static str = "rust_client"; @@ -232,13 +246,49 @@ impl LightstreamerClient { &self.subscriptions } - pub fn new (server_address: &str, adapter_set: &str) -> Result<LightstreamerClient, IllegalStateException> { - let connection_details = ConnectionDetails::new( - server_address.to_string(), - adapter_set.to_string(), - ); + /// Creates a new instance of `LightstreamerClient`. + /// + /// The constructor initializes the client with the server address and adapter set, if provided. + /// It sets up the connection details and options for the client. If no server address or + /// adapter set is specified, those properties on the client will be `None`. This allows + /// for late configuration of these details before connecting to the Lightstreamer server. + /// + /// # Arguments + /// * `server_address` - An optional reference to a string slice that represents the server + /// address to connect to. If `None`, the server address must be set later. + /// * `adapter_set` - An optional reference to a string slice that specifies the adapter set name. + /// If `None`, the adapter set must be set later. + /// + /// # Returns + /// A result containing the new `LightstreamerClient` instance if successful, or an + /// `IllegalStateException` if the initialization fails due to invalid state conditions. + /// + /// # Panics + /// Does not panic under normal circumstances. However, unexpected internal errors during + /// the creation of internal components could cause panics, which should be considered when + /// using this function in production code. + /// + /// # Example + /// ``` + /// // Example usage of `new` to create a LightstreamerClient with specified server address and + /// // adapter set. + /// let server_address = Some("http://myserver.com"); + /// let adapter_set = Some("MY_ADAPTER_SET"); + /// let ls_client = LightstreamerClient::new(server_address, adapter_set); + /// + /// assert!(ls_client.is_ok()); + /// if let Ok(client) = ls_client { + /// assert_eq!(client.server_address.unwrap(), "http://myserver.com".to_string()); + /// assert_eq!(client.adapter_set.unwrap(), "MY_ADAPTER_SET".to_string()); + /// } + /// ``` + pub fn new( + server_address: Option<&str>, + adapter_set: Option<&str>, + ) -> Result<LightstreamerClient, IllegalStateException> { + let connection_details = ConnectionDetails::new(server_address, adapter_set); let connection_options = ConnectionOptions::new(); - + Ok(LightstreamerClient { server_address: server_address.map(|s| s.to_string()), adapter_set: adapter_set.map(|s| s.to_string()), diff --git a/src/main.rs b/src/main.rs index 07a3bf4..2dddb0d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,8 +108,20 @@ async fn establish_persistent_ws_connection( async fn subscribe_to_channel(session_id: String) -> Result<(), reqwest::Error> { let client = Client::new(); - let subscribe_url = "http://push.lightstreamer.com/lightstreamer/bind_session.txt"; - let params = [("LS_session", &session_id)]; + //let subscribe_url = "http://push.lightstreamer.com/lightstreamer/bind_session.txt"; + //let params = [("LS_session", &session_id)]; + let subscribe_url = + "http://push.lightstreamer.com/lightstreamer/control.txt?LS_protocol=TLCP-2.0.0"; + let params = [ + ("LS_session", &session_id), + ("LS_op", &"add".to_string()), + ("LS_subId", &"1".to_string()), + ("LS_data_adapter", &"CHAT_ROOM".to_string()), + ("LS_group", &"chat_room".to_string()), + ("LS_schema", &"timestamp message".to_string()), + ("LS_mode", &"DISTINCT".to_string()), + ("LS_reqId", &"1".to_string()), + ]; let response = client.post(subscribe_url).form(¶ms).send().await?; @@ -204,15 +216,23 @@ impl SubscriptionListener for MySubscriptionListener { async fn main() -> Result<(), Box<dyn Error>> { let mut subscription = Subscription::new( SubscriptionMode::Merge, - Some(vec!["item1".to_string(),"item2".to_string(),"item3".to_string()]), - Some(vec!["stock_name".to_string(),"last_price".to_string()]), + Some(vec![ + "item1".to_string(), + "item2".to_string(), + "item3".to_string(), + ]), + Some(vec!["stock_name".to_string(), "last_price".to_string()]), )?; subscription.add_listener(Box::new(MySubscriptionListener {})); - let client = LightstreamerClient::new("http://push.lightstreamer.com/lightstreamer", "DEMO")?; + let client = LightstreamerClient::new( + Some("http://push.lightstreamer.com/lightstreamer"), + Some("DEMO"), + )?; println!("Subscription: {:?}", subscription); + println!("Client: {:?}", client); Ok(()) } diff --git a/src/proxy.rs b/src/proxy.rs index 48b2758..9a8add8 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -1,3 +1,83 @@ +/// Simple class representing a Proxy configuration. +/// +/// An instance of this class can be used through `ConnectionOptions.setProxy()` to instruct +/// a `LightstreamerClient` to connect to the Lightstreamer Server passing through a proxy. +/// +/// # Parameters +/// +/// * `proxy_type`: the proxy type +/// * `host`: the proxy host +/// * `port`: the proxy port +/// * `user`: the user name to be used to validate against the proxy. Optional. +/// * `password`: the password to be used to validate against the proxy. Optional. +#[derive(Debug)] pub struct Proxy { + proxy_type: ProxyType, + host: String, + port: u16, + user: Option<String>, + password: Option<String>, +} +impl Proxy { + /// Creates a new instance of `Proxy`. + /// + /// # Parameters + /// + /// * `proxy_type`: the proxy type + /// * `host`: the proxy host + /// * `port`: the proxy port + /// * `user`: the user name to be used to validate against the proxy. Optional. + /// * `password`: the password to be used to validate against the proxy. Optional. + pub fn new( + proxy_type: ProxyType, + host: String, + port: u16, + user: Option<String>, + password: Option<String>, + ) -> Self { + Proxy { + proxy_type, + host, + port, + user, + password, + } + } + + /// Returns the proxy type. + pub fn get_proxy_type(&self) -> &ProxyType { + &self.proxy_type + } + + /// Returns the proxy host. + pub fn get_host(&self) -> &str { + &self.host + } + + /// Returns the proxy port. + pub fn get_port(&self) -> u16 { + self.port + } + + /// Returns the proxy user name. + pub fn get_user(&self) -> Option<&String> { + self.user.as_ref() + } + + /// Returns the proxy password. + pub fn get_password(&self) -> Option<&String> { + self.password.as_ref() + } +} + +/// Represents the type of proxy. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ProxyType { + /// HTTP proxy. + Http, + /// SOCKS4 proxy. + Socks4, + /// SOCKS5 proxy. + Socks5, }
\ No newline at end of file |