Skip to main content

easyfix_session/
acceptor.rs

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    /// Address of the peer of an active session.
294    ///
295    /// Returns `None` when the session has no connection at the moment. The
296    /// session is registered before its first message is dispatched to the
297    /// application, so this is `Some` for the whole lifetime of a connection,
298    /// including while its Logon<A> is being handled.
299    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            // Already logged out
317            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            // Already disconnected
332            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            // Already logged out
353            Ok(())
354        } else {
355            Err(AcceptorError::UnknownSession)
356        }
357    }
358
359    /// Force reset of the session
360    ///
361    /// Functionally equivalent to `reset_on_logon/logout/disconnect` settings,
362    /// but triggered manually.
363    ///
364    /// Returns [`AcceptorError::SessionActive`] if the session is still active.
365    /// In that case, call [Self::disconnect] or [Self::logout] first and wait
366    /// for the session to fully terminate before retrying.
367    #[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    // TODO: temporary solution, remove when diconnect will be synchronized
380    #[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    /// Sender seq_num getter
394    #[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    /// Override sender's next seq_num
406    #[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}