1use std::{
2 cell::{Cell, RefCell},
3 collections::HashMap,
4 future::Future,
5 io,
6 net::SocketAddr,
7 pin::Pin,
8 rc::Rc,
9 task::{Context, Poll},
10};
11
12use easyfix_messages::fields::{FixString, SeqNum, SessionStatus};
13use futures::{self, Stream};
14use pin_project::pin_project;
15use tokio::{
16 io::{AsyncRead, AsyncWrite},
17 net::TcpListener,
18 task::JoinHandle,
19};
20use tracing::{Instrument, error, info, info_span, instrument, warn};
21
22use crate::{
23 DisconnectReason, Settings,
24 application::{AsEvent, Emitter, EventStream, events_channel},
25 io::{PendingLogout, acceptor_connection, supervise_connection},
26 messages_storage::MessagesStorage,
27 session::Session,
28 session_id::SessionId,
29 session_state::State as SessionState,
30 settings::SessionSettings,
31};
32
33#[derive(Debug, thiserror::Error)]
34pub enum AcceptorError {
35 #[error("Unknown session")]
36 UnknownSession,
37 #[error("Session active")]
38 SessionActive,
39}
40
41#[allow(async_fn_in_trait)]
42pub trait Connection {
43 async fn accept(
44 &mut self,
45 ) -> Result<
46 (
47 impl AsyncRead + Unpin + 'static,
48 impl AsyncWrite + Unpin + 'static,
49 SocketAddr,
50 ),
51 io::Error,
52 >;
53}
54
55pub struct TcpConnection {
56 listener: TcpListener,
57}
58
59impl TcpConnection {
60 pub async fn new(socket_addr: impl Into<SocketAddr>) -> Result<TcpConnection, io::Error> {
61 let socket_addr = socket_addr.into();
62 let listener = TcpListener::bind(&socket_addr).await?;
63 Ok(TcpConnection { listener })
64 }
65}
66
67impl Connection for TcpConnection {
68 async fn accept(
69 &mut self,
70 ) -> Result<
71 (
72 impl AsyncRead + Unpin + 'static,
73 impl AsyncWrite + Unpin + 'static,
74 SocketAddr,
75 ),
76 io::Error,
77 > {
78 let (tcp_stream, peer_addr) = self.listener.accept().await?;
79 tcp_stream.set_nodelay(true)?;
80 let (reader, writer) = tcp_stream.into_split();
81 Ok((reader, writer, peer_addr))
82 }
83}
84
85type SessionMapInternal<S> = HashMap<SessionId, (SessionSettings, Rc<RefCell<SessionState<S>>>)>;
86
87pub struct SessionsMap<S> {
88 map: SessionMapInternal<S>,
89 message_storage_builder: Box<dyn Fn(&SessionId) -> S>,
90}
91
92impl<S: MessagesStorage> SessionsMap<S> {
93 fn new(message_storage_builder: Box<dyn Fn(&SessionId) -> S>) -> SessionsMap<S> {
94 SessionsMap {
95 map: HashMap::new(),
96 message_storage_builder,
97 }
98 }
99
100 pub fn register_session(&mut self, session_id: SessionId, session_settings: SessionSettings) {
101 let storage = (self.message_storage_builder)(&session_id);
102 self.map.insert(
103 session_id.clone(),
104 (
105 session_settings,
106 Rc::new(RefCell::new(SessionState::new(storage))),
107 ),
108 );
109 }
110
111 pub(crate) fn get_session(
112 &self,
113 session_id: &SessionId,
114 ) -> Option<(SessionSettings, Rc<RefCell<SessionState<S>>>)> {
115 self.map.get(session_id).cloned()
116 }
117
118 fn contains(&self, session_id: &SessionId) -> bool {
119 self.map.contains_key(session_id)
120 }
121}
122
123pub struct SessionTask<S> {
124 settings: Settings,
125 sessions: Rc<RefCell<SessionsMap<S>>>,
126 active_sessions: Rc<RefCell<ActiveSessionsMap<S>>>,
127 emitter: Emitter,
128 enabled: Rc<Cell<bool>>,
129}
130
131impl<S> Clone for SessionTask<S> {
132 fn clone(&self) -> Self {
133 Self {
134 settings: self.settings.clone(),
135 sessions: self.sessions.clone(),
136 active_sessions: self.active_sessions.clone(),
137 emitter: self.emitter.clone(),
138 enabled: self.enabled.clone(),
139 }
140 }
141}
142
143impl<S: MessagesStorage + 'static> SessionTask<S> {
144 fn new(
145 settings: Settings,
146 sessions: Rc<RefCell<SessionsMap<S>>>,
147 active_sessions: Rc<RefCell<ActiveSessionsMap<S>>>,
148 emitter: Emitter,
149 enabled: Rc<Cell<bool>>,
150 ) -> SessionTask<S> {
151 SessionTask {
152 settings,
153 sessions,
154 active_sessions,
155 emitter,
156 enabled,
157 }
158 }
159
160 pub async fn run(
161 self,
162 peer_addr: SocketAddr,
163 reader: impl AsyncRead + Unpin + 'static,
164 writer: impl AsyncWrite + Unpin + 'static,
165 ) {
166 let span = info_span!("connection", %peer_addr);
167
168 span.in_scope(|| {
169 info!("New connection");
170 });
171
172 if self.enabled.get() {
173 let pending_logout = PendingLogout::default();
174 supervise_connection(
175 acceptor_connection(
176 reader,
177 writer,
178 self.settings,
179 self.sessions,
180 self.active_sessions,
181 self.emitter.clone(),
182 self.enabled,
183 pending_logout.clone(),
184 peer_addr,
185 ),
186 pending_logout,
187 &self.emitter,
188 )
189 .instrument(span.clone())
190 .await;
191 } else {
192 span.in_scope(|| warn!("Acceptor is disabled"))
193 }
194
195 span.in_scope(|| {
196 info!("Connection closed");
197 });
198 }
199}
200
201pub(crate) type ActiveSessionsMap<S> = HashMap<SessionId, Rc<Session<S>>>;
202
203#[pin_project]
204pub struct Acceptor<S> {
205 sessions: Rc<RefCell<SessionsMap<S>>>,
206 active_sessions: Rc<RefCell<ActiveSessionsMap<S>>>,
207 session_task: SessionTask<S>,
208 #[pin]
209 event_stream: EventStream,
210 enabled: Rc<Cell<bool>>,
211}
212
213impl<S: MessagesStorage + 'static> Acceptor<S> {
214 pub fn new(
215 settings: Settings,
216 message_storage_builder: Box<dyn Fn(&SessionId) -> S>,
217 ) -> Acceptor<S> {
218 let (emitter, event_stream) = events_channel();
219 let sessions = Rc::new(RefCell::new(SessionsMap::new(message_storage_builder)));
220 let active_sessions = Rc::new(RefCell::new(HashMap::new()));
221 let enabled = Rc::new(Cell::new(true));
222 let session_task = SessionTask::new(
223 settings,
224 sessions.clone(),
225 active_sessions.clone(),
226 emitter,
227 enabled.clone(),
228 );
229
230 Acceptor {
231 sessions,
232 active_sessions,
233 session_task,
234 event_stream,
235 enabled,
236 }
237 }
238
239 pub fn enable(&self) {
240 info!("acceptor enabled");
241 self.enabled.set(true);
242 }
243
244 pub fn disable(&self) {
245 info!("acceptor disabled");
246 self.enabled.set(false);
247 for (_, session) in self.active_sessions.borrow_mut().drain() {
248 session.disconnect(
249 &mut session.state().borrow_mut(),
250 DisconnectReason::ApplicationForcedDisconnect,
251 );
252 }
253 }
254
255 pub fn disable_with_logout(
256 &self,
257 session_status: Option<SessionStatus>,
258 reason: Option<FixString>,
259 ) {
260 info!("acceptor disabled with logout");
261 self.enabled.set(false);
262 for (_, session) in self.active_sessions.borrow_mut().drain() {
263 let mut state = session.state().borrow_mut();
264 session.send_logout(&mut state, session_status, reason.clone());
265 session.disconnect(&mut state, DisconnectReason::ApplicationForcedDisconnect);
266 }
267 }
268
269 pub fn register_session(&mut self, session_id: SessionId, session_settings: SessionSettings) {
270 self.sessions
271 .borrow_mut()
272 .register_session(session_id, session_settings);
273 }
274
275 pub fn sessions_map(&self) -> Rc<RefCell<SessionsMap<S>>> {
276 self.sessions.clone()
277 }
278
279 pub fn start(&self, connection: impl Connection + 'static) -> JoinHandle<()> {
280 tokio::task::spawn_local(Self::server_task(connection, self.session_task.clone()))
281 }
282
283 pub fn is_session_active(&self, session_id: &SessionId) -> Result<bool, AcceptorError> {
284 if self.active_sessions.borrow().contains_key(session_id) {
285 Ok(true)
286 } else if self.sessions.borrow().contains(session_id) {
287 Ok(false)
288 } else {
289 Err(AcceptorError::UnknownSession)
290 }
291 }
292
293 pub fn peer_addr(&self, session_id: &SessionId) -> Option<SocketAddr> {
300 self.active_sessions
301 .borrow()
302 .get(session_id)
303 .map(|session| session.peer_addr())
304 }
305
306 pub fn logout(
307 &self,
308 session_id: &SessionId,
309 session_status: Option<SessionStatus>,
310 reason: Option<FixString>,
311 ) -> Result<(), AcceptorError> {
312 if let Some(session) = self.active_sessions.borrow().get(session_id) {
313 session.send_logout(&mut session.state().borrow_mut(), session_status, reason);
314 Ok(())
315 } else if self.sessions.borrow().contains(session_id) {
316 Ok(())
318 } else {
319 Err(AcceptorError::UnknownSession)
320 }
321 }
322
323 pub fn disconnect(&self, session_id: &SessionId) -> Result<(), AcceptorError> {
324 if let Some(session) = self.active_sessions.borrow_mut().remove(session_id) {
325 session.disconnect(
326 &mut session.state().borrow_mut(),
327 DisconnectReason::ApplicationForcedDisconnect,
328 );
329 Ok(())
330 } else if self.sessions.borrow().contains(session_id) {
331 Ok(())
333 } else {
334 Err(AcceptorError::UnknownSession)
335 }
336 }
337
338 pub fn disconnect_with_logout(
339 &self,
340 session_id: &SessionId,
341 session_status: Option<SessionStatus>,
342 reason: Option<FixString>,
343 ) -> Result<(), AcceptorError> {
344 if let Some(session) = self.active_sessions.borrow().get(session_id) {
345 session.send_logout(&mut session.state().borrow_mut(), session_status, reason);
346 session.disconnect(
347 &mut session.state().borrow_mut(),
348 DisconnectReason::ApplicationForcedDisconnect,
349 );
350 Ok(())
351 } else if self.sessions.borrow().contains(session_id) {
352 Ok(())
354 } else {
355 Err(AcceptorError::UnknownSession)
356 }
357 }
358
359 #[instrument(skip_all, fields(session_id=%session_id) ret)]
368 pub fn reset(&self, session_id: &SessionId) -> Result<(), AcceptorError> {
369 if self.active_sessions.borrow().contains_key(session_id) {
370 Err(AcceptorError::SessionActive)
371 } else if let Some((_, session_state)) = self.sessions.borrow().get_session(session_id) {
372 session_state.borrow_mut().reset();
373 Ok(())
374 } else {
375 Err(AcceptorError::UnknownSession)
376 }
377 }
378
379 #[instrument(skip_all, fields(session_id=%session_id) ret)]
381 pub fn force_reset(&self, session_id: &SessionId) -> Result<(), AcceptorError> {
382 if let Some(session) = self.active_sessions.borrow().get(session_id) {
383 session.state().borrow_mut().reset();
384 Ok(())
385 } else if let Some((_, session_state)) = self.sessions.borrow().get_session(session_id) {
386 session_state.borrow_mut().reset();
387 Ok(())
388 } else {
389 Err(AcceptorError::UnknownSession)
390 }
391 }
392
393 #[instrument(skip_all, fields(session_id=%session_id) ret)]
395 pub fn next_sender_msg_seq_num(&self, session_id: &SessionId) -> Result<SeqNum, AcceptorError> {
396 if let Some(session) = self.active_sessions.borrow().get(session_id) {
397 Ok(session.state().borrow().next_sender_msg_seq_num())
398 } else if let Some((_, session_state)) = self.sessions.borrow().get_session(session_id) {
399 Ok(session_state.borrow().next_sender_msg_seq_num())
400 } else {
401 Err(AcceptorError::UnknownSession)
402 }
403 }
404
405 #[instrument(skip_all, fields(session_id=%session_id, seq_num) ret)]
407 pub fn set_next_sender_msg_seq_num(
408 &self,
409 session_id: &SessionId,
410 seq_num: SeqNum,
411 ) -> Result<(), AcceptorError> {
412 if let Some(session) = self.active_sessions.borrow().get(session_id) {
413 session
414 .state()
415 .borrow_mut()
416 .set_next_sender_msg_seq_num(seq_num);
417 Ok(())
418 } else if let Some((_, session_state)) = self.sessions.borrow().get_session(session_id) {
419 session_state
420 .borrow_mut()
421 .set_next_sender_msg_seq_num(seq_num);
422 Ok(())
423 } else {
424 Err(AcceptorError::UnknownSession)
425 }
426 }
427
428 async fn server_task(mut connection: impl Connection, session_task: SessionTask<S>) {
429 info!("Acceptor started");
430 loop {
431 match connection.accept().await {
432 Ok((reader, writer, peer_addr)) => {
433 tokio::task::spawn_local(session_task.clone().run(peer_addr, reader, writer));
434 }
435 Err(err) => error!("server task failed to accept incoming connection: {err}"),
436 }
437 }
438 }
439
440 pub fn session_task(&self) -> SessionTask<S> {
441 self.session_task.clone()
442 }
443
444 pub fn run_session_task(
445 &self,
446 peer_addr: SocketAddr,
447 reader: impl AsyncRead + Unpin + 'static,
448 writer: impl AsyncWrite + Unpin + 'static,
449 ) -> impl Future<Output = ()> {
450 self.session_task.clone().run(peer_addr, reader, writer)
451 }
452}
453
454impl<S: MessagesStorage> Stream for Acceptor<S> {
455 type Item = impl AsEvent;
456
457 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
458 Pin::new(&mut self.event_stream).poll_next(cx)
459 }
460
461 fn size_hint(&self) -> (usize, Option<usize>) {
462 self.event_stream.size_hint()
463 }
464}