1use std::collections::BTreeMap;
9use std::sync::Arc;
10use std::time::Duration;
11
12use bytes::Bytes;
13use tokio::sync::{mpsc, oneshot};
14use tokio::time::Instant;
15
16use crate::application::{Application, ApplicationError};
17use crate::datadictionary::DataDictionary;
18use crate::error::{Error, RejectError, Result, SessionRejectReason};
19use crate::log::Log;
20use crate::message::Message;
21use crate::session_id::SessionId;
22use crate::settings::{ConnectionType, SessionConfig};
23use crate::store::MessageStore;
24use crate::tags::{self, msg_type};
25use crate::value::UtcTimestamp;
26
27pub(crate) enum Command {
29 Send(Message, oneshot::Sender<Result<()>>),
31 Attach(Connection),
33 Logout,
35 Stop(oneshot::Sender<()>),
37 Status(oneshot::Sender<SessionStatus>),
38 SetSeqNums {
41 sender: Option<u64>,
42 target: Option<u64>,
43 reply: oneshot::Sender<Result<()>>,
44 },
45}
46
47pub(crate) struct Connection {
48 pub inbound: mpsc::Receiver<Bytes>,
49 pub outbound: mpsc::Sender<Bytes>,
50 pub disconnected: Option<oneshot::Sender<()>>,
53}
54
55#[derive(Debug, Clone)]
56pub struct SessionStatus {
57 pub connected: bool,
58 pub logged_on: bool,
59 pub next_sender_seq_num: u64,
60 pub next_target_seq_num: u64,
61}
62
63#[derive(Clone)]
65pub struct SessionHandle {
66 pub id: SessionId,
67 pub(crate) cmd_tx: mpsc::Sender<Command>,
68}
69
70impl SessionHandle {
71 pub async fn send(&self, msg: Message) -> Result<()> {
74 let (tx, rx) = oneshot::channel();
75 self.cmd_tx
76 .send(Command::Send(msg, tx))
77 .await
78 .map_err(|_| Error::UnknownSession(self.id.to_string()))?;
79 rx.await.map_err(|_| Error::UnknownSession(self.id.to_string()))?
80 }
81
82 pub async fn logout(&self) -> Result<()> {
83 self.cmd_tx
84 .send(Command::Logout)
85 .await
86 .map_err(|_| Error::UnknownSession(self.id.to_string()))
87 }
88
89 pub async fn status(&self) -> Result<SessionStatus> {
90 let (tx, rx) = oneshot::channel();
91 self.cmd_tx
92 .send(Command::Status(tx))
93 .await
94 .map_err(|_| Error::UnknownSession(self.id.to_string()))?;
95 rx.await.map_err(|_| Error::UnknownSession(self.id.to_string()))
96 }
97
98 pub async fn is_logged_on(&self) -> bool {
99 self.status().await.map(|s| s.logged_on).unwrap_or(false)
100 }
101
102 pub async fn set_next_sender_seq_num(&self, n: u64) -> Result<()> {
104 self.set_seq_nums(Some(n), None).await
105 }
106
107 pub async fn set_next_target_seq_num(&self, n: u64) -> Result<()> {
109 self.set_seq_nums(None, Some(n)).await
110 }
111
112 async fn set_seq_nums(&self, sender: Option<u64>, target: Option<u64>) -> Result<()> {
113 let (reply, rx) = oneshot::channel();
114 self.cmd_tx
115 .send(Command::SetSeqNums { sender, target, reply })
116 .await
117 .map_err(|_| Error::UnknownSession(self.id.to_string()))?;
118 rx.await.map_err(|_| Error::UnknownSession(self.id.to_string()))?
119 }
120}
121
122enum Flow {
124 Continue,
127 Stop,
130}
131
132struct Disconnect(String);
134
135type Handling = std::result::Result<Flow, Disconnect>;
136
137pub(crate) struct Session {
138 cfg: SessionConfig,
139 store: Box<dyn MessageStore>,
140 log: Box<dyn Log>,
141 app: Arc<dyn Application>,
142 dictionary: Option<Arc<DataDictionary>>,
144 admin_dictionary: Option<Arc<DataDictionary>>,
147 cmd_rx: mpsc::Receiver<Command>,
148
149 inbound: Option<mpsc::Receiver<Bytes>>,
150 outbound: Option<mpsc::Sender<Bytes>>,
151 disconnected_tx: Option<oneshot::Sender<()>>,
152
153 received_logon: bool,
155 sent_logon: bool,
156 sent_logout: bool,
157 sent_reset: bool,
158 received_reset: bool,
159 heart_bt_int: Duration,
161 last_sent: Instant,
162 last_received: Instant,
163 test_request_counter: u32,
164 resend_range: Option<(u64, u64, u64)>,
169 last_received_seq: u64,
172 stash: BTreeMap<u64, Bytes>,
175 last_processed_was_queued: bool,
179 send_next_expected: bool,
181 schedule: crate::schedule::Schedule,
183 logon_schedule: crate::schedule::Schedule,
184 pending_logon: bool,
187}
188
189impl Session {
190 pub(crate) fn spawn(
191 cfg: SessionConfig,
192 store: Box<dyn MessageStore>,
193 log: Box<dyn Log>,
194 app: Arc<dyn Application>,
195 dictionary: Option<Arc<DataDictionary>>,
196 admin_dictionary: Option<Arc<DataDictionary>>,
197 ) -> SessionHandle {
198 let (cmd_tx, cmd_rx) = mpsc::channel(64);
199 let handle = SessionHandle { id: cfg.session_id.clone(), cmd_tx };
200 let heart_bt_int = cfg.heart_bt_int;
201 let send_next_expected = cfg.send_next_expected_msg_seq_num;
202 let schedule = cfg.schedule.clone();
203 let logon_schedule = cfg.logon_schedule.clone();
204 let session = Session {
205 cfg,
206 store,
207 log,
208 app,
209 dictionary,
210 admin_dictionary,
211 cmd_rx,
212 inbound: None,
213 outbound: None,
214 disconnected_tx: None,
215 received_logon: false,
216 sent_logon: false,
217 sent_logout: false,
218 sent_reset: false,
219 received_reset: false,
220 heart_bt_int,
221 last_sent: Instant::now(),
222 last_received: Instant::now(),
223 test_request_counter: 0,
224 resend_range: None,
225 last_received_seq: 0,
226 stash: BTreeMap::new(),
227 last_processed_was_queued: false,
228 send_next_expected,
229 schedule,
230 logon_schedule,
231 pending_logon: false,
232 };
233 tokio::spawn(session.run());
234 handle
235 }
236
237 fn is_initiator(&self) -> bool {
238 self.cfg.connection_type == ConnectionType::Initiator
239 }
240
241 fn is_connected(&self) -> bool {
242 self.outbound.is_some()
243 }
244
245 fn is_logged_on(&self) -> bool {
246 self.received_logon && self.sent_logon
247 }
248
249 fn event(&mut self, text: &str) {
250 self.log.on_event(text);
251 }
252
253 async fn run(mut self) {
256 self.app.on_create(&self.cfg.session_id).await;
257 let mut schedule_tick = tokio::time::interval(Duration::from_secs(1));
261 schedule_tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
262
263 loop {
264 let proto_deadline = self.next_protocol_deadline();
265 let proto_timer = async {
266 match proto_deadline {
267 Some(at) => tokio::time::sleep_until(at).await,
268 None => std::future::pending().await,
269 }
270 };
271 tokio::select! {
272 cmd = self.cmd_rx.recv() => {
273 match cmd {
274 Some(Command::Send(msg, reply)) => {
275 let res = self.send_message(msg).await;
276 let _ = reply.send(res);
277 }
278 Some(Command::Attach(conn)) => self.on_attach(conn).await,
279 Some(Command::Logout) => {
280 if self.is_logged_on() {
281 let _ = self.initiate_logout("").await;
282 }
283 }
284 Some(Command::Status(reply)) => {
285 let _ = reply.send(SessionStatus {
286 connected: self.is_connected(),
287 logged_on: self.is_logged_on(),
288 next_sender_seq_num: self.store.next_sender_seq_num(),
289 next_target_seq_num: self.store.next_target_seq_num(),
290 });
291 }
292 Some(Command::SetSeqNums { sender, target, reply }) => {
293 let mut res = Ok(());
294 if let Some(n) = sender {
295 res = res.and(self.store.set_next_sender_seq_num(n).await);
296 }
297 if let Some(n) = target {
298 res = res.and(self.store.set_next_target_seq_num(n).await);
299 }
300 let _ = reply.send(res);
301 }
302 Some(Command::Stop(reply)) => {
303 if self.is_logged_on() {
304 let _ = self.initiate_logout("").await;
305 let deadline = Instant::now() + self.cfg.logout_timeout;
307 while self.is_connected() && Instant::now() < deadline {
308 match tokio::time::timeout_at(
309 deadline,
310 recv_opt(&mut self.inbound),
311 )
312 .await
313 {
314 Ok(Some(raw)) => self.on_inbound(raw).await,
315 _ => break,
316 }
317 }
318 }
319 self.disconnect("session stopped").await;
320 let _ = reply.send(());
321 return;
322 }
323 None => {
324 self.disconnect("engine dropped").await;
325 return;
326 }
327 }
328 }
329 maybe_raw = recv_opt(&mut self.inbound) => {
330 match maybe_raw {
331 Some(raw) => self.on_inbound(raw).await,
332 None => self.disconnect("connection closed by peer").await,
333 }
334 }
335 _ = schedule_tick.tick() => self.on_schedule_tick().await,
336 _ = proto_timer => self.on_protocol_deadline().await,
337 }
338 }
339 }
340
341 async fn on_attach(&mut self, conn: Connection) {
342 if self.is_connected() {
343 self.event("Rejecting connection attempt: session already connected");
344 return;
346 }
347 self.inbound = Some(conn.inbound);
348 self.outbound = Some(conn.outbound);
349 self.disconnected_tx = conn.disconnected;
350 self.last_sent = Instant::now();
351 self.last_received = Instant::now();
352 self.test_request_counter = 0;
353 self.event("Connection established");
354
355 if self.is_initiator() {
356 if self.cfg.reset_on_logon {
357 let _ = self.store.reset().await;
358 }
359 if self.cfg.refresh_on_logon {
360 let _ = self.store.refresh().await;
361 }
362 if self.logon_schedule.is_in_range(chrono::Utc::now()) {
365 if let Err(Disconnect(reason)) = self.send_logon().await {
366 self.disconnect(&reason).await;
367 }
368 } else {
369 self.pending_logon = true;
370 self.event("Connected outside logon time; deferring logon");
371 }
372 }
373 }
374
375 async fn disconnect(&mut self, reason: &str) {
376 if !self.is_connected() && self.inbound.is_none() {
377 return;
378 }
379 self.event(&format!("Disconnecting: {reason}"));
380 let was_logged_on = self.received_logon || self.sent_logon;
381 self.inbound = None;
382 self.outbound = None;
383 self.disconnected_tx = None; self.received_logon = false;
385 self.sent_logon = false;
386 self.sent_logout = false;
387 self.sent_reset = false;
388 self.received_reset = false;
389 self.test_request_counter = 0;
390 self.resend_range = None;
391 self.stash.clear();
392 if self.cfg.reset_on_disconnect {
393 let _ = self.store.reset().await;
394 }
395 if was_logged_on {
396 self.app.on_logout(&self.cfg.session_id).await;
397 }
398 }
399
400 fn next_protocol_deadline(&self) -> Option<Instant> {
413 if !self.is_connected() {
414 return None;
415 }
416 if self.sent_logout {
417 return Some(self.last_sent + self.cfg.logout_timeout);
418 }
419 if !self.received_logon {
420 return Some(self.last_received + self.cfg.logon_timeout);
421 }
422 let hbi = self.heart_bt_int;
423 if hbi.is_zero() {
424 return None;
425 }
426 let mut deadline = (self.last_received + mul(hbi, 2.4))
429 .min(self.last_received + mul(hbi, 1.2 * (self.test_request_counter + 1) as f64));
430 if self.test_request_counter == 0 {
431 deadline = deadline.min(self.last_sent + hbi);
432 }
433 Some(deadline)
434 }
435
436 async fn on_protocol_deadline(&mut self) {
438 if !self.is_connected() {
439 return;
440 }
441 let now = Instant::now();
442 let since_sent = now.duration_since(self.last_sent);
443 let since_recv = now.duration_since(self.last_received);
444
445 if self.sent_logout {
448 if since_sent >= self.cfg.logout_timeout {
449 self.disconnect("Timed out waiting for logout response").await;
450 }
451 return;
452 }
453 if !self.received_logon {
454 if since_recv >= self.cfg.logon_timeout {
457 self.disconnect("Timed out waiting for logon").await;
458 }
459 return;
460 }
461 let hbi = self.heart_bt_int;
462 if hbi.is_zero() {
463 return;
464 }
465 if since_recv >= mul(hbi, 2.4) {
466 if self.cfg.send_logout_before_disconnect_from_timeout {
467 let _ = self.initiate_logout("").await;
468 }
469 self.disconnect("Timed out waiting for heartbeat").await;
470 } else if since_recv >= mul(hbi, 1.2 * (self.test_request_counter + 1) as f64) {
471 self.test_request_counter += 1;
472 let mut tr = Message::with_type(msg_type::TEST_REQUEST);
473 tr.set(tags::TEST_REQ_ID, "TEST");
474 let _ = self.send_message(tr).await;
475 } else if since_sent >= hbi && self.test_request_counter == 0 {
476 let _ = self.send_message(Message::with_type(msg_type::HEARTBEAT)).await;
477 }
478 }
479
480 async fn on_schedule_tick(&mut self) {
484 if self.schedule.is_non_stop() {
485 return;
486 }
487 let utc_now = chrono::Utc::now();
488 if !self.schedule.is_in_range(utc_now) {
489 if self.is_connected() {
492 if self.is_logged_on() && !self.sent_logout {
493 let _ = self.initiate_logout("").await;
494 }
495 self.disconnect("Outside session time").await;
496 }
497 return;
498 }
499 if !self.schedule.is_in_same_range(utc_now, self.store.creation_time()) {
503 self.event("New session instance; resetting sequence numbers");
504 if self.is_connected() {
505 if self.is_logged_on() && !self.sent_logout {
506 let _ = self.initiate_logout("").await;
507 }
508 self.disconnect("Session time boundary crossed").await;
509 }
510 let _ = self.store.reset().await;
511 return;
512 }
513 if self.is_connected()
515 && self.is_logged_on()
516 && !self.sent_logout
517 && !self.logon_schedule.is_in_range(utc_now)
518 {
519 self.event("Logon time expired, initiating logout");
520 let _ = self.initiate_logout("").await;
521 }
522 if self.pending_logon
524 && self.is_connected()
525 && !self.sent_logon
526 && self.logon_schedule.is_in_range(utc_now)
527 {
528 self.pending_logon = false;
529 if let Err(Disconnect(reason)) = self.send_logon().await {
530 self.disconnect(&reason).await;
531 }
532 }
533 }
534
535 fn fill_header(&mut self, msg: &mut Message) {
539 let id = &self.cfg.session_id;
540 msg.header.set(tags::BEGIN_STRING, id.begin_string.as_str());
541 msg.header.set(tags::SENDER_COMP_ID, id.sender_comp_id.as_str());
542 msg.header.set(tags::TARGET_COMP_ID, id.target_comp_id.as_str());
543 if !id.sender_sub_id.is_empty() {
544 msg.header.set(tags::SENDER_SUB_ID, id.sender_sub_id.as_str());
545 }
546 if !id.sender_location_id.is_empty() {
547 msg.header.set(tags::SENDER_LOCATION_ID, id.sender_location_id.as_str());
548 }
549 if !id.target_sub_id.is_empty() {
550 msg.header.set(tags::TARGET_SUB_ID, id.target_sub_id.as_str());
551 }
552 if !id.target_location_id.is_empty() {
553 msg.header.set(tags::TARGET_LOCATION_ID, id.target_location_id.as_str());
554 }
555 msg.header.set(tags::MSG_SEQ_NUM, self.store.next_sender_seq_num());
556 if self.cfg.enable_last_msg_seq_num_processed {
557 msg.header.set(tags::LAST_MSG_SEQ_NUM_PROCESSED, self.last_received_seq);
558 }
559 msg.stamp_sending_time(UtcTimestamp::new(
560 chrono::Utc::now(),
561 self.cfg.timestamp_precision,
562 ));
563 }
564
565 async fn send_message(&mut self, mut msg: Message) -> Result<()> {
567 msg.header.remove(tags::POSS_DUP_FLAG);
569 msg.header.remove(tags::ORIG_SENDING_TIME);
570 self.fill_header(&mut msg);
571 let is_admin = msg.is_admin();
572 let mt = msg.msg_type().unwrap_or_default();
573 if is_admin {
574 self.app.to_admin(&mut msg, &self.cfg.session_id).await;
575 } else if self.app.to_app(&mut msg, &self.cfg.session_id).await.is_err() {
576 return Err(Error::DoNotSend);
577 }
578 if let Some(dd) = &self.dictionary {
579 dd.canonicalize_body(&mut msg);
580 }
581 let seq = msg.seq_num()?;
582 let raw = msg.to_bytes();
583 if self.cfg.persist_messages {
584 self.store.save_message_and_incr(seq, &raw).await?;
585 } else {
586 self.store.incr_next_sender_seq_num().await?;
587 }
588 let always = matches!(
591 mt.as_str(),
592 msg_type::LOGON | msg_type::LOGOUT | msg_type::RESEND_REQUEST | msg_type::SEQUENCE_RESET
593 );
594 if self.is_logged_on() || always || self.sent_logon {
595 self.transmit(raw.into()).await;
596 }
597 Ok(())
598 }
599
600 async fn transmit(&mut self, raw: Bytes) {
603 self.log.on_outgoing(&raw);
604 if let Some(out) = &self.outbound {
605 if out.send(raw).await.is_err() {
606 self.disconnect("write side closed").await;
607 return;
608 }
609 self.last_sent = Instant::now();
610 }
611 }
612
613 async fn send_logon(&mut self) -> std::result::Result<(), Disconnect> {
614 let mut logon = Message::with_type(msg_type::LOGON);
615 logon.set(tags::ENCRYPT_METHOD, 0);
616 logon.set(tags::HEART_BT_INT, self.heart_bt_int.as_secs());
617 let should_reset = self.cfg.send_reset_seq_num_flag
618 || (self.cfg.session_id.begin_string.as_str() >= "FIX.4.1"
619 && self.cfg.reset_on_logon
620 && self.store.next_sender_seq_num() == 1
621 && self.store.next_target_seq_num() == 1);
622 if should_reset {
623 logon.set(tags::RESET_SEQ_NUM_FLAG, true);
624 self.sent_reset = true;
625 }
626 if let Some(v) = &self.cfg.default_appl_ver_id {
627 logon.set(tags::DEFAULT_APPL_VER_ID, appl_ver_id_enum(v));
628 }
629 if self.send_next_expected {
633 logon.set(tags::NEXT_EXPECTED_MSG_SEQ_NUM, self.store.next_target_seq_num());
634 }
635 self.sent_logon = true;
636 self.event("Initiated logon request");
637 self.send_message(logon)
638 .await
639 .map_err(|e| Disconnect(format!("failed to send logon: {e}")))
640 }
641
642 async fn send_logon_reply(&mut self, peer_hbi: Option<u64>) -> Result<()> {
643 if let Some(secs) = peer_hbi {
644 self.heart_bt_int = self
645 .cfg
646 .heart_bt_int_override
647 .unwrap_or(Duration::from_secs(secs));
648 }
649 let mut logon = Message::with_type(msg_type::LOGON);
650 logon.set(tags::ENCRYPT_METHOD, 0);
651 logon.set(tags::HEART_BT_INT, self.heart_bt_int.as_secs());
652 if self.received_reset {
657 if self.send_next_expected {
658 logon.set(tags::RESET_SEQ_NUM_FLAG, true);
659 }
660 self.sent_reset = true;
661 }
662 if let Some(v) = &self.cfg.default_appl_ver_id {
663 logon.set(tags::DEFAULT_APPL_VER_ID, appl_ver_id_enum(v));
664 }
665 if self.send_next_expected {
669 logon.set(tags::NEXT_EXPECTED_MSG_SEQ_NUM, self.store.next_target_seq_num() + 1);
670 }
671 self.sent_logon = true;
672 self.event("Responding to logon request");
673 self.send_message(logon).await
674 }
675
676 async fn initiate_logout(&mut self, reason: &str) -> Result<()> {
677 let mut logout = Message::with_type(msg_type::LOGOUT);
678 if !reason.is_empty() {
679 logout.set(tags::TEXT, reason);
680 self.event(&format!("Initiated logout: {reason}"));
681 } else {
682 self.event("Initiated logout request");
683 }
684 self.sent_logout = true;
685 self.send_message(logout).await
686 }
687
688 async fn send_reject(&mut self, offender: &Message, rej: &RejectError) -> Result<()> {
692 let mt = offender.msg_type().unwrap_or_default();
693 if mt != msg_type::LOGON
694 && mt != msg_type::SEQUENCE_RESET
695 && offender.seq_num().ok() == Some(self.store.next_target_seq_num())
696 {
697 self.store.incr_next_target_seq_num().await?;
698 }
699 let fix42_plus = self.cfg.session_id.begin_string.as_str() >= "FIX.4.2"
700 || self.cfg.session_id.is_fixt();
701 let mut reject = Message::with_type(msg_type::REJECT);
703 reverse_route(offender, &mut reject, &self.cfg.session_id.begin_string);
704 if let Ok(seq) = offender.seq_num() {
705 reject.set(tags::REF_SEQ_NUM, seq);
706 }
707 let text = match rej.ref_tag {
710 Some(tag) if !fix42_plus && rej.text.is_none() => format!("{rej} ({tag})"),
711 _ => rej.to_string(),
712 };
713 reject.set(tags::TEXT, text.as_str());
714 if fix42_plus {
715 if let Some(tag) = rej.ref_tag {
716 reject.set(tags::REF_TAG_ID, tag);
717 }
718 if let Ok(mt) = offender.msg_type() {
719 reject.set(tags::REF_MSG_TYPE, mt.as_str());
720 }
721 let code = rej.reason.code();
724 if code <= 11 || self.cfg.session_id.begin_string.as_str() > "FIX.4.2" {
725 reject.set(tags::SESSION_REJECT_REASON, code);
726 }
727 }
728 self.event(&format!("Message rejected: {rej}"));
729 self.send_message(reject).await
730 }
731
732 async fn send_business_reject(&mut self, offender: &Message) -> Result<()> {
733 self.store.incr_next_target_seq_num().await?;
734 let fix42_plus = self.cfg.session_id.begin_string.as_str() >= "FIX.4.2"
735 || self.cfg.session_id.is_fixt();
736 let (mut reject, text) = if fix42_plus {
741 (Message::with_type(msg_type::BUSINESS_MESSAGE_REJECT), "Unsupported Message Type")
742 } else {
743 (Message::with_type(msg_type::REJECT), "Unsupported message type")
744 };
745 reverse_route(offender, &mut reject, &self.cfg.session_id.begin_string);
746 if let Ok(seq) = offender.seq_num() {
747 reject.set(tags::REF_SEQ_NUM, seq);
748 }
749 reject.set(tags::TEXT, text);
750 if fix42_plus {
751 if let Ok(mt) = offender.msg_type() {
752 reject.set(tags::REF_MSG_TYPE, mt.as_str());
753 }
754 reject.set(tags::BUSINESS_REJECT_REASON, 3u32); }
756 self.send_message(reject).await
757 }
758
759 async fn send_resend_request(&mut self, begin: u64, received: u64) -> Result<()> {
760 let full_end = received - 1;
761 let mut rr = Message::with_type(msg_type::RESEND_REQUEST);
762 rr.set(tags::BEGIN_SEQ_NO, begin);
763 let chunk_end = match self.cfg.max_messages_in_resend_request {
764 0 => {
766 let open_ended = self.cfg.session_id.begin_string.as_str() >= "FIX.4.2"
767 || self.cfg.session_id.is_fixt();
768 rr.set(tags::END_SEQ_NO, if open_ended { 0 } else { 999999u64 });
769 full_end
770 }
771 max => {
772 let chunk_end = full_end.min(begin + max - 1);
773 rr.set(tags::END_SEQ_NO, chunk_end);
774 chunk_end
775 }
776 };
777 self.resend_range = Some((begin, full_end, chunk_end));
778 self.event(&format!("Sent ResendRequest FROM: {begin} TO: {chunk_end}"));
779 self.send_message(rr).await
780 }
781
782 async fn check_resend_chunks(&mut self) {
785 let Some((_, full_end, chunk_end)) = self.resend_range else { return };
786 let expected = self.store.next_target_seq_num();
787 if expected <= chunk_end {
788 return;
789 }
790 if expected <= full_end {
791 let _ = self.send_resend_request(expected, full_end + 1).await;
792 } else {
793 self.event(&format!("ResendRequest for messages FROM ... TO {full_end} has been satisfied"));
794 self.resend_range = None;
795 }
796 }
797
798 async fn send_gap_fill(&mut self, begin: u64, new_seq: u64) {
801 let mut m = Message::with_type(msg_type::SEQUENCE_RESET);
802 self.fill_header(&mut m);
803 m.header.set(tags::MSG_SEQ_NUM, begin);
804 m.header.set(tags::POSS_DUP_FLAG, true);
805 let now = UtcTimestamp::new(chrono::Utc::now(), self.cfg.timestamp_precision);
806 m.header.set(tags::ORIG_SENDING_TIME, now);
807 m.set(tags::NEW_SEQ_NO, new_seq);
808 m.set(tags::GAP_FILL_FLAG, true);
809 self.app.to_admin(&mut m, &self.cfg.session_id).await;
810 self.event(&format!("Sent SequenceReset (GapFill) {begin} -> {new_seq}"));
811 let raw = m.to_bytes();
812 self.transmit(raw.into()).await;
813 }
814
815 async fn on_inbound(&mut self, raw: Bytes) {
818 self.log.on_incoming(&raw);
819 let msg = match Message::parse(&raw, self.cfg.validate_length_checksum) {
820 Ok(m) => m,
821 Err(e) => {
822 self.event(&format!("Invalid message: {e}"));
824 if !self.received_logon && contains_field(&raw, b"35=A") {
826 self.disconnect("garbled logon").await;
827 }
828 return;
829 }
830 };
831 if let Ok(seq) = msg.seq_num() {
832 self.last_received_seq = seq;
833 }
834 if let Err(Disconnect(reason)) = self.process(msg, raw).await {
835 self.disconnect(&reason).await;
836 return;
837 }
838 self.last_processed_was_queued = false;
839 if let Err(Disconnect(reason)) = self.drain_stash().await {
840 self.disconnect(&reason).await;
841 return;
842 }
843 self.check_resend_chunks().await;
844 }
845
846 async fn process(&mut self, msg: Message, raw: Bytes) -> std::result::Result<(), Disconnect> {
848 let mt = match msg.msg_type() {
849 Ok(mt) => mt,
850 Err(_) => {
851 self.event("Message without MsgType ignored");
852 return Ok(());
853 }
854 };
855 if msg.header.get_raw(tags::BEGIN_STRING)
857 != Some(self.cfg.session_id.begin_string.as_bytes())
858 {
859 let got = msg
860 .header
861 .get_raw(tags::BEGIN_STRING)
862 .map(|v| String::from_utf8_lossy(v).into_owned())
863 .unwrap_or_default();
864 let _ = self.store.incr_next_target_seq_num().await;
865 let _ = self.initiate_logout(&format!("Incorrect BeginString ({got})")).await;
866 return Err(Disconnect("incorrect BeginString".into()));
867 }
868
869 let dd = if msg.is_admin() { &self.admin_dictionary } else { &self.dictionary };
874 if let Some(dd) = dd.clone() {
875 if let Err(rej) = dd.validate(&msg, &self.cfg.validation) {
876 let _ = self.send_reject(&msg, &rej).await;
877 return Ok(());
878 }
879 }
880
881 match mt.as_str() {
882 msg_type::LOGON => self.handle_logon(&msg).await,
883 msg_type::HEARTBEAT | msg_type::REJECT => self.handle_plain_admin(&msg).await,
884 msg_type::TEST_REQUEST => self.handle_test_request(&msg).await,
885 msg_type::RESEND_REQUEST => self.handle_resend_request(&msg).await,
886 msg_type::SEQUENCE_RESET => self.handle_sequence_reset(&msg).await,
887 msg_type::LOGOUT => self.handle_logout(&msg).await,
888 _ => self.handle_app_message(&msg, raw).await,
889 }
890 }
891
892 async fn verify(
895 &mut self,
896 msg: &Message,
897 check_too_high: bool,
898 check_too_low: bool,
899 ) -> Handling {
900 let mt = msg.msg_type().unwrap_or_default();
901
902 if !self.valid_logon_state(&mt) {
903 return Err(Disconnect(format!("logon state invalid for message type {mt}")));
904 }
905
906 match msg.header.get_opt::<UtcTimestamp>(tags::SENDING_TIME) {
908 Ok(Some(st)) => {
909 if self.cfg.check_latency {
910 let delta = (chrono::Utc::now() - st.time).abs();
911 if delta.num_seconds().unsigned_abs()
912 > self.cfg.max_latency.as_secs()
913 {
914 let _ = self
915 .send_reject(
916 msg,
917 &RejectError::new(
918 SessionRejectReason::SendingTimeAccuracyProblem,
919 ),
920 )
921 .await;
922 let _ = self.initiate_logout("").await;
923 return Ok(Flow::Stop);
924 }
925 }
926 }
927 _ => {
928 let _ = self
929 .send_reject(
930 msg,
931 &RejectError::with_tag(
932 SessionRejectReason::RequiredTagMissing,
933 tags::SENDING_TIME,
934 ),
935 )
936 .await;
937 return Ok(Flow::Stop);
938 }
939 }
940
941 if self.cfg.check_comp_id {
943 let sender = msg.header.get_raw(tags::SENDER_COMP_ID).unwrap_or_default();
944 let target = msg.header.get_raw(tags::TARGET_COMP_ID).unwrap_or_default();
945 if sender != self.cfg.session_id.target_comp_id.as_bytes()
946 || target != self.cfg.session_id.sender_comp_id.as_bytes()
947 {
948 let _ = self
949 .send_reject(msg, &RejectError::new(SessionRejectReason::CompIDProblem))
950 .await;
951 let _ = self.initiate_logout("").await;
952 return Ok(Flow::Stop);
953 }
954 }
955
956 if check_too_high || check_too_low {
958 let Ok(seq) = msg.seq_num() else {
959 let _ = self
960 .send_reject(
961 msg,
962 &RejectError::with_tag(
963 SessionRejectReason::RequiredTagMissing,
964 tags::MSG_SEQ_NUM,
965 ),
966 )
967 .await;
968 return Ok(Flow::Stop);
969 };
970 let expected = self.store.next_target_seq_num();
971 if check_too_high && seq > expected {
972 self.on_target_too_high(msg, seq, expected).await?;
973 return Ok(Flow::Stop);
974 }
975 if check_too_low && seq < expected {
976 let obey_anyway = self.last_processed_was_queued
979 && mt == msg_type::SEQUENCE_RESET
980 && msg.body.get_raw(tags::GAP_FILL_FLAG) == Some(b"Y");
981 if obey_anyway {
982 self.event(&format!(
983 "SequenceReset-GapFill {seq} is too low (expected {expected}), obeying it anyway"
984 ));
985 } else {
986 return self.on_target_too_low(msg, seq, expected).await;
987 }
988 }
989 }
990
991 self.last_received = Instant::now();
992 self.test_request_counter = 0;
993
994 let cb = if msg.is_admin() {
996 self.app.from_admin(msg, &self.cfg.session_id).await
997 } else {
998 self.app.from_app(msg, &self.cfg.session_id).await
999 };
1000 match cb {
1001 Ok(()) => Ok(Flow::Continue),
1002 Err(ApplicationError::RejectLogon(reason)) => {
1003 let _ = self.store.incr_next_target_seq_num().await;
1004 let _ = self.initiate_logout(&reason).await;
1005 Err(Disconnect(format!("logon rejected by application: {reason}")))
1006 }
1007 Err(ApplicationError::Reject(rej)) => {
1008 let _ = self.send_reject(msg, &rej).await;
1009 Ok(Flow::Stop)
1010 }
1011 Err(ApplicationError::UnsupportedMessageType) => {
1012 let _ = self.send_business_reject(msg).await;
1013 Ok(Flow::Stop)
1014 }
1015 }
1016 }
1017
1018 fn valid_logon_state(&self, mt: &str) -> bool {
1021 (mt == msg_type::LOGON && (self.sent_reset || self.received_reset))
1022 || (mt == msg_type::LOGON && !self.received_logon)
1023 || (mt != msg_type::LOGON && self.received_logon)
1024 || (mt == msg_type::LOGOUT && self.sent_logon)
1025 || (mt != msg_type::LOGOUT && self.sent_logout)
1026 || mt == msg_type::SEQUENCE_RESET
1027 || mt == msg_type::REJECT
1028 }
1029
1030 async fn on_target_too_high(
1031 &mut self,
1032 msg: &Message,
1033 seq: u64,
1034 expected: u64,
1035 ) -> std::result::Result<(), Disconnect> {
1036 self.event(&format!("MsgSeqNum too high, expecting {expected} but received {seq}"));
1037 self.stash.insert(seq, Bytes::from(msg.to_bytes()));
1039 let suppress = self.resend_range.is_some() && !self.cfg.send_redundant_resend_requests;
1040 if !suppress {
1041 self.send_resend_request(expected, seq)
1042 .await
1043 .map_err(|e| Disconnect(format!("failed to send ResendRequest: {e}")))?;
1044 }
1045 Ok(())
1046 }
1047
1048 async fn on_target_too_low(&mut self, msg: &Message, seq: u64, expected: u64) -> Handling {
1049 if !msg.poss_dup() {
1050 let reason =
1051 format!("MsgSeqNum too low, expecting {expected} but received {seq}");
1052 let _ = self.initiate_logout(&reason).await;
1053 return Err(Disconnect(reason));
1054 }
1055 let mt = msg.msg_type().unwrap_or_default();
1057 if mt != msg_type::SEQUENCE_RESET && self.cfg.requires_orig_sending_time {
1058 match msg.header.get_opt::<UtcTimestamp>(tags::ORIG_SENDING_TIME) {
1059 Ok(Some(orig)) => {
1060 let sending = msg.header.get_opt::<UtcTimestamp>(tags::SENDING_TIME).ok().flatten();
1061 if let Some(st) = sending {
1062 if orig.time > st.time {
1063 let _ = self
1064 .send_reject(
1065 msg,
1066 &RejectError::new(
1067 SessionRejectReason::SendingTimeAccuracyProblem,
1068 ),
1069 )
1070 .await;
1071 let _ = self.initiate_logout("").await;
1072 return Ok(Flow::Stop);
1073 }
1074 }
1075 }
1076 _ => {
1077 let _ = self
1078 .send_reject(
1079 msg,
1080 &RejectError::with_tag(
1081 SessionRejectReason::RequiredTagMissing,
1082 tags::ORIG_SENDING_TIME,
1083 ),
1084 )
1085 .await;
1086 return Ok(Flow::Stop);
1087 }
1088 }
1089 }
1090 self.event(&format!("Already received message {seq}, ignoring PossDup"));
1091 Ok(Flow::Stop)
1092 }
1093
1094 async fn handle_logon(&mut self, msg: &Message) -> std::result::Result<(), Disconnect> {
1097 if self.cfg.check_latency {
1100 let good = msg
1101 .header
1102 .get_opt::<UtcTimestamp>(tags::SENDING_TIME)
1103 .ok()
1104 .flatten()
1105 .is_some_and(|st| {
1106 (chrono::Utc::now() - st.time).abs().num_seconds().unsigned_abs()
1107 <= self.cfg.max_latency.as_secs()
1108 });
1109 if !good {
1110 return Err(Disconnect("logon has bad sending time".into()));
1111 }
1112 }
1113 if msg.body.get_raw(tags::RESET_SEQ_NUM_FLAG) == Some(b"Y") {
1115 self.received_reset = true;
1116 if !self.sent_reset {
1117 self.event("Logon contains ResetSeqNumFlag=Y, resetting sequence numbers to 1");
1118 self.store
1119 .reset()
1120 .await
1121 .map_err(|e| Disconnect(format!("store reset failed: {e}")))?;
1122 }
1123 }
1124 if self.is_initiator() && !self.sent_logon {
1126 return Err(Disconnect("received logon response before sending request".into()));
1127 }
1128 if !self.is_initiator() {
1129 if self.cfg.refresh_on_logon {
1130 let _ = self.store.refresh().await;
1131 }
1132 if self.cfg.reset_on_logon && !self.received_reset {
1133 let _ = self.store.reset().await;
1134 }
1135 }
1136
1137 match self.verify(msg, false, true).await? {
1138 Flow::Stop => return Ok(()),
1139 Flow::Continue => {}
1140 }
1141 self.received_logon = true;
1142
1143 let mut retransmit_from: Option<u64> = None;
1146 if self.send_next_expected {
1147 if let Ok(Some(peer_789)) = msg.body.get_opt::<u64>(tags::NEXT_EXPECTED_MSG_SEQ_NUM) {
1148 let next_sender = self.store.next_sender_seq_num();
1149 if peer_789 > next_sender {
1150 let reason = format!(
1152 "Tag 789 (NextExpectedMsgSeqNum) is higher than expected. Expected {next_sender}, Received {peer_789}"
1153 );
1154 let _ = self.initiate_logout(&reason).await;
1155 return Err(Disconnect(reason));
1156 } else if peer_789 < next_sender {
1157 retransmit_from = Some(peer_789);
1159 }
1160 }
1161 }
1162
1163 if !self.is_initiator() {
1164 let peer_hbi = msg.body.get_opt::<u64>(tags::HEART_BT_INT).ok().flatten();
1165 self.send_logon_reply(peer_hbi)
1166 .await
1167 .map_err(|e| Disconnect(format!("failed to send logon reply: {e}")))?;
1168 } else {
1169 self.event("Received logon response");
1170 }
1171 self.sent_reset = false;
1172 self.received_reset = false;
1173
1174 let seq = msg.seq_num().map_err(|_| Disconnect("logon missing MsgSeqNum".into()))?;
1176 let expected = self.store.next_target_seq_num();
1177 let reset = msg.body.get_raw(tags::RESET_SEQ_NUM_FLAG) == Some(b"Y");
1178 if seq > expected {
1179 if self.send_next_expected && !reset {
1180 self.event(&format!(
1185 "Expecting retransmits FROM: {expected} TO: {}",
1186 seq - 1
1187 ));
1188 self.stash.insert(seq, Bytes::from(msg.to_bytes()));
1189 self.resend_range = Some((expected, seq - 1, seq - 1));
1190 } else {
1191 self.on_target_too_high(msg, seq, expected).await?;
1192 }
1193 } else {
1194 self.store
1195 .incr_next_target_seq_num()
1196 .await
1197 .map_err(|e| Disconnect(format!("store error: {e}")))?;
1198 }
1199
1200 if self.is_logged_on() {
1201 self.event("Logon successful");
1202 self.app.on_logon(&self.cfg.session_id).await;
1203 }
1204
1205 if let Some(begin) = retransmit_from {
1207 let end = self.store.next_sender_seq_num() - 1;
1208 self.event(&format!(
1209 "Sending retransmits due to received NextExpectedMsgSeqNum too low. FROM: {begin} TO: {end}"
1210 ));
1211 self.answer_resend(begin, end).await;
1212 }
1213 Ok(())
1214 }
1215
1216 async fn handle_plain_admin(&mut self, msg: &Message) -> std::result::Result<(), Disconnect> {
1218 if let Flow::Continue = self.verify(msg, true, true).await? {
1219 self.incr_target().await?;
1220 }
1221 Ok(())
1222 }
1223
1224 async fn handle_test_request(&mut self, msg: &Message) -> std::result::Result<(), Disconnect> {
1225 if let Flow::Continue = self.verify(msg, true, true).await? {
1226 let mut hb = Message::with_type(msg_type::HEARTBEAT);
1227 if let Some(id) = msg.body.get_raw(tags::TEST_REQ_ID) {
1228 hb.body.set_raw(tags::TEST_REQ_ID, id.to_vec());
1229 }
1230 let _ = self.send_message(hb).await;
1231 self.incr_target().await?;
1232 }
1233 Ok(())
1234 }
1235
1236 async fn handle_resend_request(&mut self, msg: &Message) -> std::result::Result<(), Disconnect> {
1237 if let Flow::Stop = self.verify(msg, false, false).await? {
1238 return Ok(());
1239 }
1240 let begin: u64 = msg.body.get_opt(tags::BEGIN_SEQ_NO).ok().flatten().unwrap_or(1);
1241 let mut end: u64 = msg.body.get_opt(tags::END_SEQ_NO).ok().flatten().unwrap_or(0);
1242 let next_sender = self.store.next_sender_seq_num();
1243 if end == 0 || end == 999999 || end >= next_sender {
1244 end = next_sender - 1;
1245 }
1246 self.event(&format!("Received ResendRequest FROM: {begin} TO: {end}"));
1247 self.answer_resend(begin, end).await;
1248
1249 if let Ok(seq) = msg.seq_num() {
1252 if seq == self.store.next_target_seq_num() {
1253 self.incr_target().await?;
1254 }
1255 }
1256 Ok(())
1257 }
1258
1259 async fn answer_resend(&mut self, begin: u64, end: u64) {
1263 if begin > end {
1264 return;
1265 }
1266 if self.cfg.persist_messages {
1267 self.retransmit(begin, end).await;
1268 } else {
1269 self.send_gap_fill(begin, end + 1).await;
1270 }
1271 }
1272
1273 async fn retransmit(&mut self, begin: u64, end: u64) {
1276 let stored: BTreeMap<u64, Vec<u8>> = match self.store.get_messages(begin, end).await {
1277 Ok(v) => v.into_iter().collect(),
1278 Err(e) => {
1279 self.event(&format!("Store error during resend: {e}"));
1280 return;
1281 }
1282 };
1283 let now = UtcTimestamp::new(chrono::Utc::now(), self.cfg.timestamp_precision);
1284 let mut gap_begin: Option<u64> = None;
1285 for seq in begin..=end {
1286 let resend = stored.get(&seq).and_then(|raw| Message::parse(raw, false).ok());
1287 match resend {
1288 Some(mut m) if !m.is_admin() => {
1289 m.header.set(tags::POSS_DUP_FLAG, true);
1290 if let Some(orig) = m.header.get_raw(tags::SENDING_TIME).map(|v| v.to_vec()) {
1291 m.header.set_raw(tags::ORIG_SENDING_TIME, orig);
1292 }
1293 m.stamp_sending_time(now);
1294 if self.app.to_app(&mut m, &self.cfg.session_id).await.is_err() {
1295 gap_begin.get_or_insert(seq);
1296 continue;
1297 }
1298 if let Some(gb) = gap_begin.take() {
1299 self.send_gap_fill(gb, seq).await;
1300 }
1301 self.event(&format!("Resending message {seq}"));
1302 let raw = m.to_bytes();
1303 self.transmit(raw.into()).await;
1304 }
1305 _ => {
1307 gap_begin.get_or_insert(seq);
1308 }
1309 }
1310 }
1311 if let Some(gb) = gap_begin.take() {
1312 self.send_gap_fill(gb, end + 1).await;
1313 }
1314 }
1315
1316 async fn handle_sequence_reset(&mut self, msg: &Message) -> std::result::Result<(), Disconnect> {
1317 let gap_fill = msg.body.get_raw(tags::GAP_FILL_FLAG) == Some(b"Y");
1318 if let Flow::Stop = self.verify(msg, gap_fill, gap_fill).await? {
1320 return Ok(());
1321 }
1322 if let Ok(Some(new_seq)) = msg.body.get_opt::<u64>(tags::NEW_SEQ_NO) {
1323 let expected = self.store.next_target_seq_num();
1324 self.event(&format!("Received SequenceReset (GapFill={}) NewSeqNo={new_seq}",
1325 if gap_fill { "Y" } else { "N" }));
1326 if new_seq > expected {
1327 self.store
1328 .set_next_target_seq_num(new_seq)
1329 .await
1330 .map_err(|e| Disconnect(format!("store error: {e}")))?;
1331 self.stash.retain(|&k, _| k >= new_seq);
1333 } else if new_seq < expected {
1334 let _ = self
1335 .send_reject(msg, &RejectError::new(SessionRejectReason::ValueIsIncorrect))
1336 .await;
1337 }
1338 }
1339 Ok(())
1340 }
1341
1342 async fn handle_logout(&mut self, msg: &Message) -> std::result::Result<(), Disconnect> {
1343 if let Flow::Stop = self.verify(msg, false, false).await? {
1344 return Ok(());
1345 }
1346 if self.sent_logout {
1347 self.event("Received logout response");
1348 } else {
1349 self.event("Received logout request");
1350 let _ = self.initiate_logout("").await;
1351 }
1352 self.incr_target().await?;
1353 if self.cfg.reset_on_logout {
1354 let _ = self.store.reset().await;
1355 }
1356 Err(Disconnect("logout complete".into()))
1357 }
1358
1359 async fn handle_app_message(
1360 &mut self,
1361 msg: &Message,
1362 _raw: Bytes,
1363 ) -> std::result::Result<(), Disconnect> {
1364 if let Flow::Continue = self.verify(msg, true, true).await? {
1365 self.incr_target().await?;
1366 }
1367 Ok(())
1368 }
1369
1370 async fn incr_target(&mut self) -> std::result::Result<(), Disconnect> {
1371 self.store
1372 .incr_next_target_seq_num()
1373 .await
1374 .map_err(|e| Disconnect(format!("store error: {e}")))
1375 }
1376
1377 async fn drain_stash(&mut self) -> std::result::Result<(), Disconnect> {
1379 loop {
1380 let expected = self.store.next_target_seq_num();
1381 let Some(raw) = self.stash.remove(&expected) else { return Ok(()) };
1382 self.event(&format!("Processing queued message: {expected}"));
1383 self.last_processed_was_queued = true;
1384 if contains_field(&raw, b"35=A") || contains_field(&raw, b"35=2") {
1387 self.incr_target().await?;
1388 continue;
1389 }
1390 match Message::parse(&raw, false) {
1391 Ok(msg) => self.process(msg, raw).await?,
1392 Err(_) => self.incr_target().await?,
1393 }
1394 }
1395 }
1396}
1397
1398async fn recv_opt(rx: &mut Option<mpsc::Receiver<Bytes>>) -> Option<Bytes> {
1399 match rx {
1400 Some(rx) => rx.recv().await,
1401 None => std::future::pending().await,
1402 }
1403}
1404
1405fn mul(d: Duration, f: f64) -> Duration {
1406 Duration::from_secs_f64(d.as_secs_f64() * f)
1407}
1408
1409fn appl_ver_id_enum(configured: &str) -> &str {
1412 match configured {
1413 "FIX.2.7" => "0",
1414 "FIX.3.0" => "1",
1415 "FIX.4.0" => "2",
1416 "FIX.4.1" => "3",
1417 "FIX.4.2" => "4",
1418 "FIX.4.3" => "5",
1419 "FIX.4.4" => "6",
1420 "FIX.5.0" => "7",
1421 "FIX.5.0SP1" => "8",
1422 "FIX.5.0SP2" => "9",
1423 other => other, }
1425}
1426
1427fn reverse_route(offender: &Message, reply: &mut Message, begin_string: &str) {
1431 const PAIRS: [(crate::message::Tag, crate::message::Tag); 6] = [
1432 (tags::ON_BEHALF_OF_COMP_ID, tags::DELIVER_TO_COMP_ID),
1433 (tags::ON_BEHALF_OF_SUB_ID, tags::DELIVER_TO_SUB_ID),
1434 (tags::ON_BEHALF_OF_LOCATION_ID, tags::DELIVER_TO_LOCATION_ID),
1435 (tags::DELIVER_TO_COMP_ID, tags::ON_BEHALF_OF_COMP_ID),
1436 (tags::DELIVER_TO_SUB_ID, tags::ON_BEHALF_OF_SUB_ID),
1437 (tags::DELIVER_TO_LOCATION_ID, tags::ON_BEHALF_OF_LOCATION_ID),
1438 ];
1439 let has_location_ids = begin_string >= "FIX.4.1";
1441 for (src, dst) in PAIRS {
1442 if !has_location_ids
1443 && matches!(src, tags::ON_BEHALF_OF_LOCATION_ID | tags::DELIVER_TO_LOCATION_ID)
1444 {
1445 continue;
1446 }
1447 if let Some(v) = offender.header.get_raw(src) {
1448 if !v.is_empty() {
1449 reply.header.set_raw(dst, v.to_vec());
1450 }
1451 }
1452 }
1453}
1454
1455fn contains_field(raw: &[u8], field: &[u8]) -> bool {
1457 if raw.starts_with(field) {
1458 return true;
1459 }
1460 let mut needle = Vec::with_capacity(field.len() + 1);
1461 needle.push(crate::message::SOH);
1462 needle.extend_from_slice(field);
1463 raw.windows(needle.len()).any(|w| w == needle)
1464}