easyfix_session/
lib.rs

1#![feature(impl_trait_in_assoc_type)]
2
3pub mod acceptor;
4pub mod application;
5pub mod initiator;
6pub mod io;
7pub mod messages_storage;
8mod session;
9pub mod session_id;
10mod session_state;
11pub mod settings;
12
13use std::time::Duration;
14
15use easyfix_messages::{
16    fields::{FixString, MsgType, UtcTimestamp},
17    messages::{FixtMessage, Header, Message, Trailer},
18};
19use settings::Settings;
20use tokio::sync::mpsc;
21
22const NO_INBOUND_TIMEOUT_PADDING: Duration = Duration::from_millis(250);
23
24use tracing::error;
25
26#[derive(Debug, thiserror::Error)]
27pub enum SessionError {
28    #[error("Never received logon from new connection.")]
29    LogonNeverReceived,
30    #[error("Message does not point to any session.")]
31    UnknownSession,
32}
33
34#[derive(Debug, thiserror::Error)]
35pub enum Error {
36    #[error("I/O error: {0}")]
37    Io(#[from] std::io::Error),
38    #[error("Session error: {0}")]
39    SessionError(SessionError),
40}
41
42/// Disconnection reasons.
43#[derive(Clone, Copy, Debug)]
44pub enum DisconnectReason {
45    /// Logout requested locally
46    LocalRequestedLogout,
47    /// Logout requested remotely
48    RemoteRequestedLogout,
49    /// Disconnect forced by User code
50    UserForcedDisconnect,
51    /// Received message without MsgSeqNum
52    MsgSeqNumNotFound,
53    /// Received message with MsgSeqNum too low
54    MsgSeqNumTooLow,
55    /// Invalid logon state
56    InvalidLogonState,
57    /// Remote side disconnected
58    Disconnected,
59    /// I/O Error
60    IoError,
61}
62
63#[derive(Debug)]
64pub(crate) enum SenderMsg {
65    Msg(Box<FixtMessage>),
66    Disconnect(DisconnectReason),
67}
68
69#[derive(Clone, Debug)]
70pub struct Sender {
71    inner: mpsc::UnboundedSender<SenderMsg>,
72}
73
74impl Sender {
75    /// Create new `Sender` instance.
76    pub(crate) fn new(writer: mpsc::UnboundedSender<SenderMsg>) -> Sender {
77        Sender { inner: writer }
78    }
79
80    /// Send FIXT message.
81    ///
82    /// All header and trailer fields can be also adjusted when handing
83    /// `FixEvent::AppMsgOut` and `FixEvent::AdmMsgOut`.
84    ///
85    /// Before serialziation following header fields will be filled:
86    /// - begin_string (if not empty)
87    /// - msg_type
88    /// - sender_comp_id (if not empty)
89    /// - target_comp_id (if not empty)
90    /// - sending_time (if eq UtcTimestamp::MIN_UTC)
91    /// - msg_seq_num (if eq 0)
92    ///
93    /// The checksum(10) field value is always ignored - it is computed and set
94    /// after serialziation.
95    pub fn send_raw(&self, msg: Box<FixtMessage>) -> Result<(), Box<FixtMessage>> {
96        if let Err(msg) = self.inner.send(SenderMsg::Msg(msg)) {
97            match msg.0 {
98                SenderMsg::Msg(msg) => {
99                    error!(
100                        "failed to send {:?}<{}> message, receiver closed or dropped",
101                        msg.msg_type(),
102                        msg.msg_type().as_fix_str()
103                    );
104                    Err(msg)
105                }
106                SenderMsg::Disconnect(_) => unreachable!(),
107            }
108        } else {
109            Ok(())
110        }
111    }
112
113    /// Send FIX message.
114    ///
115    /// FIXT message will be constructed internally using default values
116    /// for Header and Trailer.
117    ///
118    /// All header and trailer fields can be also adjusted when handing
119    /// `FixEvent::AppMsgOut` and `FixEvent::AdmMsgOut`.
120    pub fn send(&self, msg: Box<Message>) -> Result<(), Box<FixtMessage>> {
121        let msg = Box::new(FixtMessage {
122            header: Box::new(new_header(msg.msg_type())),
123            body: msg,
124            trailer: Box::new(new_trailer()),
125        });
126        self.send_raw(msg)
127    }
128
129    /// Send disconnect message.
130    ///
131    /// Output stream will close output queue so no more message can be send
132    /// after this one.
133    pub(crate) fn disconnect(&self, reason: DisconnectReason) {
134        if self.inner.send(SenderMsg::Disconnect(reason)).is_err() {
135            error!("failed to disconnect, receiver closed or dropped");
136        }
137    }
138}
139
140pub fn new_header(msg_type: MsgType) -> Header {
141    // XXX: all required fields overwritten before serialization (if not set)
142    Header {
143        begin_string: FixString::new(),
144        msg_type,
145        sending_time: UtcTimestamp::MIN_UTC,
146        ..Default::default()
147    }
148}
149
150pub fn new_trailer() -> Trailer {
151    // XXX: all required fields overwritten before serialization
152    Trailer::default()
153}