steamkit/networking/steam3/
web_socket_connection.rs1use std::net::{IpAddr, SocketAddr};
7use std::sync::{Arc, Mutex, MutexGuard};
8use std::time::Duration;
9
10use async_trait::async_trait;
11use bytes::Bytes;
12use tokio::sync::{broadcast, mpsc};
13
14use crate::error::SteamError;
15
16use super::disconnected_event_args::DisconnectedEventArgs;
17use super::i_connection::{ConnectionEvent, ConnectionEventSenders, ConnectionEvents, IConnection};
18use super::net_msg_event_args::NetMsgEventArgs;
19use super::protocol_type::ProtocolTypes;
20use super::web_socket_context::{
21 construct_uri, construct_uri_for_host, TungsteniteWebSocketFactory, WebSocketContext,
22 WebSocketFactory, WebSocketSocket,
23};
24
25#[derive(Clone)]
26struct CurrentConnection {
27 context: Arc<WebSocketContext>,
28 socket: Arc<dyn WebSocketSocket>,
29}
30
31pub struct WebSocketConnection {
33 events: ConnectionEventSenders,
34 factory: Arc<dyn WebSocketFactory>,
35 host: Option<String>,
36 current: Arc<Mutex<Option<CurrentConnection>>>,
37 current_end_point: Arc<Mutex<Option<SocketAddr>>>,
38}
39
40impl std::fmt::Debug for WebSocketConnection {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 f.debug_struct("WebSocketConnection").finish_non_exhaustive()
43 }
44}
45
46impl WebSocketConnection {
47 pub fn new(factory: Arc<dyn WebSocketFactory>) -> Self {
50 Self {
51 events: ConnectionEventSenders::with_default_capacity(),
52 factory,
53 host: None,
54 current: Arc::new(Mutex::new(None)),
55 current_end_point: Arc::new(Mutex::new(None)),
56 }
57 }
58
59 pub fn new_tungstenite() -> Self {
61 Self::new(Arc::new(TungsteniteWebSocketFactory::new()))
62 }
63
64 pub fn new_tungstenite_for_host(host: impl Into<String>) -> Self {
67 let mut connection = Self::new_tungstenite();
68 connection.host = Some(host.into());
69 connection
70 }
71
72 pub fn new_tungstenite_for_host_with_user_agent(
75 host: impl Into<String>,
76 user_agent: impl Into<String>,
77 ) -> Self {
78 let mut connection = Self::new(Arc::new(
79 TungsteniteWebSocketFactory::with_user_agent(user_agent),
80 ));
81 connection.host = Some(host.into());
82 connection
83 }
84
85 pub fn subscribe_net_msg_received(&self) -> broadcast::Receiver<NetMsgEventArgs> {
87 self.events.subscribe_net_msg_received()
88 }
89
90 pub fn subscribe_connected(&self) -> broadcast::Receiver<()> {
92 self.events.subscribe_connected()
93 }
94
95 pub fn subscribe_disconnected(&self) -> broadcast::Receiver<DisconnectedEventArgs> {
97 self.events.subscribe_disconnected()
98 }
99}
100
101#[async_trait]
102impl IConnection for WebSocketConnection {
103 fn current_end_point(&self) -> Option<SocketAddr> {
104 lock(&self.current_end_point).clone()
105 }
106
107 fn protocol_types(&self) -> ProtocolTypes {
108 ProtocolTypes::WEB_SOCKET
109 }
110
111 async fn connect(&self, end_point: SocketAddr, timeout: Duration) -> Result<(), SteamError> {
112 let uri = match &self.host {
113 Some(host) => construct_uri_for_host(host, end_point.port()),
114 None => construct_uri(end_point),
115 };
116
117 let socket = match self.factory.connect(&uri, timeout).await {
118 Ok(socket) => socket,
119 Err(err) => {
120 tracing::debug!("WebSocketConnection: Exception connecting websocket: {err}");
121 self.events
122 .emit_disconnected(DisconnectedEventArgs::new(false));
123 return Err(err);
124 }
125 };
126
127 let context = Arc::new(WebSocketContext::new_with_uri(
128 end_point,
129 uri,
130 self.events.clone(),
131 ));
132
133 let old = {
134 let mut current = lock(&self.current);
135 current.replace(CurrentConnection {
136 context: context.clone(),
137 socket: socket.clone(),
138 })
139 };
140
141 if let Some(old) = old {
142 tracing::debug!(
143 "WebSocketConnection: Attempted to connect while already connected. Closing old connection..."
144 );
145 old.context.cancel();
146 let _ = old.socket.close().await;
147 self.events
148 .emit_disconnected(DisconnectedEventArgs::new(false));
149 }
150
151 *lock(&self.current_end_point) = Some(end_point);
152
153 context.start(socket);
154
155 Ok(())
156 }
157
158 async fn disconnect(&self, user_initiated: bool) {
159 let current = {
160 let mut guard = lock(&self.current);
161 guard.take()
162 };
163
164 if let Some(current) = current {
165 current.context.cancel();
166 current.context.notify_disconnected(user_initiated);
167 let _ = current.socket.close().await;
168 }
169
170 *lock(&self.current_end_point) = None;
171 }
172
173 async fn send(&self, data: Bytes) -> Result<(), SteamError> {
174 let current = lock(&self.current).clone();
175
176 let Some(current) = current else {
177 return Ok(());
178 };
179
180 match current.socket.send_binary(data).await {
181 Ok(()) => Ok(()),
182 Err(err) => {
183 tracing::debug!("WebSocketConnection: Exception while sending data: {err}");
184 self.disconnect(false).await;
185 Err(err)
186 }
187 }
188 }
189
190 fn local_ip(&self) -> Option<IpAddr> {
191 Some(IpAddr::V4(std::net::Ipv4Addr::new(255, 255, 255, 255)))
195 }
196}
197
198impl ConnectionEvents for WebSocketConnection {
199 fn events(&self) -> mpsc::UnboundedReceiver<ConnectionEvent> {
200 self.events.subscribe_events()
201 }
202
203 fn net_msg_received(&self) -> broadcast::Receiver<NetMsgEventArgs> {
204 self.events.subscribe_net_msg_received()
205 }
206
207 fn connected(&self) -> broadcast::Receiver<()> {
208 self.events.subscribe_connected()
209 }
210
211 fn disconnected(&self) -> broadcast::Receiver<DisconnectedEventArgs> {
212 self.events.subscribe_disconnected()
213 }
214}
215
216fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
217 match mutex.lock() {
218 Ok(guard) => guard,
219 Err(poisoned) => poisoned.into_inner(),
220 }
221}