Skip to content

Commit aa5cb67

Browse files
committed
Move handle_verification_failure out of session
1 parent 4f687f4 commit aa5cb67

8 files changed

Lines changed: 145 additions & 187 deletions

File tree

crates/hotfix/src/session.rs

Lines changed: 60 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,11 @@ use crate::message::reject::Reject;
3434
use crate::message::resend_request::ResendRequest;
3535
use crate::message::sequence_reset::SequenceReset;
3636
use crate::message::test_request::TestRequest;
37-
use crate::message::verification_issue::VerificationIssue;
3837
use crate::session::admin_request::AdminRequest;
39-
use crate::session::ctx::{SessionCtx, TransitionResult};
38+
use crate::session::ctx::{SessionCtx, TransitionResult, VerificationResult};
4039
use crate::session::error::SessionCreationError;
4140
use crate::session::error::{InternalSendError, InternalSendResultExt, SessionOperationError};
4241
pub use crate::session::error::{SendError, SendOutcome};
43-
use crate::session::inbound::verify_message_with_ctx;
4442
pub use crate::session::info::{SessionInfo, Status};
4543
pub use crate::session::session_handle::SessionHandle;
4644
#[cfg(not(feature = "test-utils"))]
@@ -49,9 +47,7 @@ pub(crate) use crate::session::session_ref::InternalSessionRef;
4947
pub use crate::session::session_ref::InternalSessionRef;
5048
use crate::session::session_ref::OutboundRequest;
5149
use crate::session::state::SessionState;
52-
use crate::session::state::{
53-
AwaitingLogonState, AwaitingLogoutState, AwaitingResendTransitionOutcome, TestRequestId,
54-
};
50+
use crate::session::state::{AwaitingLogonState, AwaitingLogoutState, TestRequestId};
5551
use crate::session_schedule::{SessionPeriodComparison, SessionSchedule};
5652
use crate::store::MessageStore;
5753
use crate::transport::writer::WriterRef;
@@ -253,8 +249,15 @@ where
253249
&mut self,
254250
message: &Message,
255251
) -> Result<(), SessionOperationError> {
256-
match verify_message_with_ctx(&self.ctx, message, true, true) {
257-
Ok(_) => {
252+
match self
253+
.state
254+
.handle_verification_issue(&mut self.ctx, message, true, true)
255+
.await?
256+
{
257+
VerificationResult::Issue(result) => {
258+
self.apply_transition(result);
259+
}
260+
VerificationResult::Passed => {
258261
match self.ctx.application.on_inbound_message(message).await {
259262
InboundDecision::Accept => {}
260263
InboundDecision::Reject { reason, text } => {
@@ -278,7 +281,6 @@ where
278281
}
279282
self.ctx.store.increment_target_seq_number().await?;
280283
}
281-
Err(err) => self.handle_verification_failure(err).await?,
282284
}
283285

284286
Ok(())
@@ -357,17 +359,22 @@ where
357359

358360
async fn on_logon(&mut self, message: &Message) -> Result<(), SessionOperationError> {
359361
if let SessionState::AwaitingLogon(AwaitingLogonState { writer, .. }) = &self.state {
360-
match verify_message_with_ctx(&self.ctx, message, true, true) {
361-
Ok(_) => {
362+
let writer = writer.clone();
363+
match self
364+
.state
365+
.handle_verification_issue(&mut self.ctx, message, true, true)
366+
.await?
367+
{
368+
VerificationResult::Issue(result) => {
369+
self.apply_transition(result);
370+
}
371+
VerificationResult::Passed => {
362372
// happy logon flow, the session is now active
363-
self.state = SessionState::new_active(
364-
writer.clone(),
365-
self.ctx.config.heartbeat_interval,
366-
);
373+
self.state =
374+
SessionState::new_active(writer, self.ctx.config.heartbeat_interval);
367375
self.ctx.application.on_logon().await;
368376
self.ctx.store.increment_target_seq_number().await?;
369377
}
370-
Err(err) => self.handle_verification_failure(err).await?,
371378
}
372379
} else {
373380
error!("received unexpected logon message");
@@ -377,8 +384,12 @@ where
377384
}
378385

379386
async fn on_logout(&mut self, message: &Message) -> Result<(), SessionOperationError> {
380-
if let Err(err) = verify_message_with_ctx(&self.ctx, message, false, false) {
381-
self.handle_verification_failure(err).await?;
387+
if let VerificationResult::Issue(result) = self
388+
.state
389+
.handle_verification_issue(&mut self.ctx, message, false, false)
390+
.await?
391+
{
392+
self.apply_transition(result);
382393
return Ok(());
383394
}
384395

@@ -413,8 +424,12 @@ where
413424
}
414425

415426
async fn on_heartbeat(&mut self, message: &Message) -> Result<(), SessionOperationError> {
416-
if let Err(err) = verify_message_with_ctx(&self.ctx, message, true, true) {
417-
self.handle_verification_failure(err).await?;
427+
if let VerificationResult::Issue(result) = self
428+
.state
429+
.handle_verification_issue(&mut self.ctx, message, true, true)
430+
.await?
431+
{
432+
self.apply_transition(result);
418433
return Ok(());
419434
}
420435

@@ -432,8 +447,12 @@ where
432447
}
433448

434449
async fn on_test_request(&mut self, message: &Message) -> Result<(), SessionOperationError> {
435-
if let Err(err) = verify_message_with_ctx(&self.ctx, message, true, true) {
436-
self.handle_verification_failure(err).await?;
450+
if let VerificationResult::Issue(result) = self
451+
.state
452+
.handle_verification_issue(&mut self.ctx, message, true, true)
453+
.await?
454+
{
455+
self.apply_transition(result);
437456
return Ok(());
438457
}
439458

@@ -461,12 +480,13 @@ where
461480
// This is the key part of the QFJ-673 deadlock fix: when both sides send ResendRequest
462481
// simultaneously, each side's ResendRequest will have a seq number higher than expected.
463482
// By not treating that as an error, we allow the ResendRequest to be processed.
464-
match verify_message_with_ctx(&self.ctx, message, false, true) {
465-
Ok(_) => {}
466-
Err(err) => {
467-
self.handle_verification_failure(err).await?;
468-
return Ok(());
469-
}
483+
if let VerificationResult::Issue(result) = self
484+
.state
485+
.handle_verification_issue(&mut self.ctx, message, false, true)
486+
.await?
487+
{
488+
self.apply_transition(result);
489+
return Ok(());
470490
}
471491

472492
let msg_seq_num = get_msg_seq_num(message);
@@ -529,8 +549,12 @@ where
529549

530550
/// Handle Reject messages.
531551
async fn on_reject(&mut self, message: &Message) -> Result<(), SessionOperationError> {
532-
if let Err(err) = verify_message_with_ctx(&self.ctx, message, false, true) {
533-
self.handle_verification_failure(err).await?;
552+
if let VerificationResult::Issue(result) = self
553+
.state
554+
.handle_verification_issue(&mut self.ctx, message, false, true)
555+
.await?
556+
{
557+
self.apply_transition(result);
534558
return Ok(());
535559
}
536560

@@ -541,8 +565,12 @@ where
541565
async fn on_sequence_reset(&mut self, message: &Message) -> Result<(), SessionOperationError> {
542566
let msg_seq_num = get_msg_seq_num(message);
543567
let is_gap_fill: bool = message.get(GAP_FILL_FLAG).unwrap_or(false);
544-
if let Err(err) = verify_message_with_ctx(&self.ctx, message, is_gap_fill, is_gap_fill) {
545-
self.handle_verification_failure(err).await?;
568+
if let VerificationResult::Issue(result) = self
569+
.state
570+
.handle_verification_issue(&mut self.ctx, message, is_gap_fill, is_gap_fill)
571+
.await?
572+
{
573+
self.apply_transition(result);
546574
return Ok(());
547575
}
548576

@@ -588,64 +616,6 @@ where
588616
Ok(())
589617
}
590618

591-
async fn handle_verification_failure(
592-
&mut self,
593-
failure: VerificationIssue,
594-
) -> Result<(), SessionOperationError> {
595-
match failure {
596-
VerificationIssue::SequenceGap { expected, actual } => {
597-
self.handle_sequence_number_too_high(expected, actual)
598-
.await?;
599-
}
600-
VerificationIssue::InvalidMessage(err) => {
601-
if let Some(writer) = self.state.get_writer() {
602-
let result =
603-
inbound::handle_verification_error(&mut self.ctx, writer, err).await;
604-
self.apply_transition(result);
605-
}
606-
}
607-
}
608-
609-
Ok(())
610-
}
611-
612-
async fn handle_sequence_number_too_high(
613-
&mut self,
614-
expected: u64,
615-
actual: u64,
616-
) -> Result<(), SessionOperationError> {
617-
match self
618-
.state
619-
.try_transition_to_awaiting_resend(expected, actual)
620-
{
621-
AwaitingResendTransitionOutcome::Success => {
622-
debug!(
623-
"we are behind target (ours: {expected}, theirs: {actual}), requesting resend."
624-
);
625-
self.send_resend_request(expected, actual).await?;
626-
}
627-
AwaitingResendTransitionOutcome::InvalidState(reason) => {
628-
error!("failed to request resend: {reason}");
629-
}
630-
AwaitingResendTransitionOutcome::BeginSeqNumberTooLow => {
631-
self.state.disconnect_writer().await;
632-
self.state = SessionState::new_disconnected(
633-
false,
634-
"awaiting resend begin seq number unexpectedly lower than the previous resend request's",
635-
);
636-
}
637-
AwaitingResendTransitionOutcome::AttemptsExceeded => {
638-
self.state.disconnect_writer().await;
639-
self.state = SessionState::new_disconnected(
640-
false,
641-
"resend request attempts exceeded, manual intervention required",
642-
);
643-
}
644-
}
645-
646-
Ok(())
647-
}
648-
649619
fn apply_transition(&mut self, result: TransitionResult) {
650620
if let TransitionResult::TransitionTo(new_state) = result {
651621
self.state = new_state;
@@ -691,18 +661,6 @@ where
691661
self.state.send_message(&mut self.ctx, message).await
692662
}
693663

694-
async fn send_resend_request(
695-
&mut self,
696-
begin: u64,
697-
end: u64,
698-
) -> Result<(), SessionOperationError> {
699-
let request = ResendRequest::new(begin, end);
700-
self.send_message(request)
701-
.await
702-
.with_send_context("resend request")?;
703-
Ok(())
704-
}
705-
706664
async fn send_logon(&mut self) -> Result<(), SessionOperationError> {
707665
let reset_config = if self.ctx.config.reset_on_logon || self.reset_on_next_logon {
708666
self.ctx.store.reset().await?;

crates/hotfix/src/session/ctx.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ pub(crate) enum TransitionResult {
1515
TransitionTo(SessionState),
1616
}
1717

18+
/// The result of verifying an inbound message via a state variant's
19+
/// `handle_verification_issue` method.
20+
pub(crate) enum VerificationResult {
21+
/// Verification passed — the caller should proceed with handler logic.
22+
Passed,
23+
/// A verification issue was detected and handled. The caller should apply
24+
/// the transition and skip further processing of this message.
25+
Issue(TransitionResult),
26+
}
27+
1828
pub(crate) struct SessionCtx<A, S> {
1929
pub config: SessionConfig,
2030
pub store: S,

crates/hotfix/src/session/inbound.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use hotfix_store::MessageStore;
1313
use tracing::error;
1414
use tracing::warn;
1515

16-
pub(crate) fn verify_message_with_ctx<A, S: MessageStore>(
16+
fn verify_message_with_ctx<A, S: MessageStore>(
1717
ctx: &SessionCtx<A, S>,
1818
message: &Message,
1919
check_too_high: bool,
@@ -72,7 +72,7 @@ pub(crate) async fn verify_and_handle_errors<A, S: MessageStore>(
7272
}
7373
}
7474

75-
pub(crate) async fn handle_sending_time_accuracy_problem<A, S: MessageStore>(
75+
async fn handle_sending_time_accuracy_problem<A, S: MessageStore>(
7676
ctx: &mut SessionCtx<A, S>,
7777
writer: &WriterRef,
7878
msg_seq_num: u64,
@@ -89,7 +89,7 @@ pub(crate) async fn handle_sending_time_accuracy_problem<A, S: MessageStore>(
8989
}
9090
}
9191

92-
pub(crate) async fn handle_incorrect_begin_string<A, S: MessageStore>(
92+
async fn handle_incorrect_begin_string<A, S: MessageStore>(
9393
ctx: &mut SessionCtx<A, S>,
9494
writer: &WriterRef,
9595
received_begin_string: String,
@@ -108,7 +108,7 @@ pub(crate) async fn handle_incorrect_begin_string<A, S: MessageStore>(
108108
))
109109
}
110110

111-
pub(crate) async fn handle_incorrect_comp_id<A, S: MessageStore>(
111+
async fn handle_incorrect_comp_id<A, S: MessageStore>(
112112
ctx: &mut SessionCtx<A, S>,
113113
writer: &WriterRef,
114114
received_comp_id: String,
@@ -131,7 +131,7 @@ pub(crate) async fn handle_incorrect_comp_id<A, S: MessageStore>(
131131
TransitionResult::TransitionTo(SessionState::new_disconnected(true, "incorrect comp ID"))
132132
}
133133

134-
pub(crate) async fn handle_sequence_number_too_low<A, S: MessageStore>(
134+
async fn handle_sequence_number_too_low<A, S: MessageStore>(
135135
ctx: &mut SessionCtx<A, S>,
136136
writer: &WriterRef,
137137
expected: u64,
@@ -187,7 +187,7 @@ pub(crate) async fn handle_invalid_msg_type<A, S: MessageStore>(
187187
}
188188
}
189189

190-
pub(crate) async fn handle_original_sending_time_missing<A, S: MessageStore>(
190+
async fn handle_original_sending_time_missing<A, S: MessageStore>(
191191
ctx: &mut SessionCtx<A, S>,
192192
writer: &WriterRef,
193193
msg_seq_num: u64,
@@ -203,7 +203,7 @@ pub(crate) async fn handle_original_sending_time_missing<A, S: MessageStore>(
203203
}
204204
}
205205

206-
pub(crate) async fn handle_verification_error<A, S: MessageStore>(
206+
async fn handle_verification_error<A, S: MessageStore>(
207207
ctx: &mut SessionCtx<A, S>,
208208
writer: &WriterRef,
209209
error: MessageError,

0 commit comments

Comments
 (0)