diff --git a/.devcontainer/Dockerfile.dev b/.devcontainer/Dockerfile.dev index 0a9737b9b..6a0aa8e95 100644 --- a/.devcontainer/Dockerfile.dev +++ b/.devcontainer/Dockerfile.dev @@ -1 +1 @@ -FROM mcr.microsoft.com/devcontainers/go:1.21 \ No newline at end of file +FROM mcr.microsoft.com/devcontainers/go:1.23 \ No newline at end of file diff --git a/config/configuration.go b/config/configuration.go index a0223f9fc..2d95bf11d 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -317,6 +317,17 @@ const ( // - Y // - N ResetOnDisconnect string = "ResetOnDisconnect" + + // ResetSeqTime determines a time which a logon with a seqnum reset will be sent while keeping the session connected. + // + // Required: No + // + // Default: N/A + // + // Valid Values: + // - 00:00:00 + // - A time in the format of HH:MM:SS, time is represented in time zone configured by TimeZone + ResetSeqTime string = "ResetSeqTime" ) const ( diff --git a/internal/session_settings.go b/internal/session_settings.go index 5cbdcfb06..f679473e6 100644 --- a/internal/session_settings.go +++ b/internal/session_settings.go @@ -18,6 +18,8 @@ type SessionSettings struct { SkipCheckLatency bool MaxLatency time.Duration DisableMessagePersist bool + ResetSeqTime TimeOfDay + EnableResetSeqTime bool // Required on logon for FIX.T.1 messages. DefaultApplVerID string diff --git a/session.go b/session.go index 49b1b467b..bed249e49 100644 --- a/session.go +++ b/session.go @@ -919,6 +919,7 @@ func (s *session) run() { case now := <-ticker.C: s.CheckSessionTime(s, now) + s.CheckResetTime(s, now) } } } diff --git a/session_factory.go b/session_factory.go index effe6ed35..f0a0e5982 100644 --- a/session_factory.go +++ b/session_factory.go @@ -344,6 +344,26 @@ func (f sessionFactory) newSession( } } + if settings.HasSetting(config.ResetSeqTime) { + var seqTimeStr string + if seqTimeStr, err = settings.Setting(config.ResetSeqTime); err != nil { + return + } + + var seqTime internal.TimeOfDay + if seqTime, err = internal.ParseTimeOfDay(seqTimeStr); err != nil { + err = errors.Wrapf( + err, "problem parsing time of day '%v' for setting '%v", + settings.settings[config.StartTime], config.StartTime, + ) + return + } + s.EnableResetSeqTime = true + s.ResetSeqTime = seqTime + } else { + s.EnableResetSeqTime = false + } + if settings.HasSetting(config.TimeStampPrecision) { var precisionStr string if precisionStr, err = settings.Setting(config.TimeStampPrecision); err != nil { diff --git a/session_state.go b/session_state.go index 2c8b84d3c..c0c898b74 100644 --- a/session_state.go +++ b/session_state.go @@ -155,6 +155,15 @@ func (sm *stateMachine) CheckSessionTime(session *session, now time.Time) { } } +func (sm *stateMachine) CheckResetTime(session *session, now time.Time) { + if session.EnableResetSeqTime { + ts := internal.NewTimeOfDay(now.Clock()) + if session.ResetSeqTime == ts { + session.sendLogonInReplyTo(true, nil) + } + } +} + func (sm *stateMachine) setState(session *session, nextState sessionState) { if !nextState.IsConnected() { if sm.IsConnected() { diff --git a/session_test.go b/session_test.go index 9308ec4f0..83226fef7 100644 --- a/session_test.go +++ b/session_test.go @@ -1001,3 +1001,28 @@ func (suite *SessionSendTestSuite) TestDropAndSendDropsQueueWithReset() { suite.LastToAdminMessageSent() suite.NoMessageSent() } + +func (s *SessionSuite) TestSeqNumResetTime() { + s.MockApp.On("ToAdmin") + s.SetupTest() + + now := time.Now().UTC() + s.session.ResetSeqTime = internal.NewTimeOfDay(now.Clock()) + s.session.EnableResetSeqTime = true + + s.IncrNextSenderMsgSeqNum() + s.IncrNextTargetMsgSeqNum() + + s.MockApp.On("ToAdmin") + + s.IncrNextSenderMsgSeqNum() + s.IncrNextTargetMsgSeqNum() + + s.MockApp.On("ToAdmin") + + s.session.CheckResetTime(s.session, now) + + s.NextSenderMsgSeqNum(2) + s.NextSenderMsgSeqNum(2) + +}