diff --git a/QuickFIXn/Session.cs b/QuickFIXn/Session.cs
index a8e271b54..cb78dd002 100755
--- a/QuickFIXn/Session.cs
+++ b/QuickFIXn/Session.cs
@@ -55,7 +55,6 @@ public bool IsNewSession
}
}
-
///
/// Session setting for heartbeat interval (in seconds)
///
@@ -215,6 +214,14 @@ public TimeStampPrecision TimeStampPrecision
///
public bool RequiresOrigSendingTime { get; set; }
+ ///
+ /// True if session is waiting for ResendRequest content.
+ /// If the RR's EndSeqNo is 0 aka infinite, then this becomes
+ /// false after the first resent message is received.
+ /// Else it remains true until EndSeqNo is received.
+ ///
+ internal bool IsResendRequested => _state.ResendRequested();
+
#endregion
public Session(
@@ -927,19 +934,22 @@ public bool Verify(Message msg, bool checkTooHigh = true, bool checkTooLow = tru
return false;
}
- if (_state.ResendRequested())
+ if (IsResendRequested)
{
ResendRange range = _state.GetResendRange();
if (msgSeqNum >= range.EndSeqNo)
{
- Log.OnEvent("ResendRequest for messages FROM: " + range.BeginSeqNo + " TO: " + range.EndSeqNo + " has been satisfied.");
+ Log.OnEvent(
+ range.EndSeqNo == 0
+ ? $"ResendRequest for messages FROM: {range.BeginSeqNo} TO: {range.EndSeqNo} has been satisfied."
+ : $"ResendRequest for messages FROM: {range.BeginSeqNo} TO: {range.EndSeqNo} has been started.");
_state.SetResendRange(0, 0);
}
else if (msgSeqNum >= range.ChunkEndSeqNo)
{
Log.OnEvent("Chunked ResendRequest for messages FROM: " + range.BeginSeqNo + " TO: " + range.ChunkEndSeqNo + " has been satisfied.");
SeqNumType newChunkEndSeqNo = Math.Min(range.EndSeqNo, range.ChunkEndSeqNo + MaxMessagesInResendRequest);
- GenerateResendRequestRange(msg.Header.GetString(Fields.Tags.BeginString), range.ChunkEndSeqNo + 1, newChunkEndSeqNo);
+ GenerateResendRequestRange(msg.Header.GetString(Tags.BeginString), range.ChunkEndSeqNo + 1, newChunkEndSeqNo);
range.ChunkEndSeqNo = newChunkEndSeqNo;
}
}
@@ -1048,7 +1058,7 @@ protected void DoTargetTooHigh(Message msg, SeqNumType msgSeqNum)
Log.OnEvent("MsgSeqNum too high, expecting " + _state.NextTargetMsgSeqNum + " but received " + msgSeqNum);
_state.Queue(msgSeqNum, msg);
- if (_state.ResendRequested())
+ if (IsResendRequested)
{
ResendRange range = _state.GetResendRange();
@@ -1156,7 +1166,8 @@ protected bool GenerateResendRequestRange(string beginString, SeqNumType startSe
return false;
}
- protected void GenerateResendRequest(string beginString, SeqNumType msgSeqNum)
+ // internal so it can be unit tested
+ internal void GenerateResendRequest(string beginString, SeqNumType msgSeqNum)
{
SeqNumType beginSeqNum = _state.NextTargetMsgSeqNum;
SeqNumType endRangeSeqNum = msgSeqNum - 1;
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index dda4467e7..48ceb60a5 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -18,6 +18,7 @@ What's New
**Non-breaking changes**
* #939 - minor checkTooHigh/checkTooLow refactor in Session.cs (gbirchmeier)
+* #941 - clarify ResendRequest-related log message, add UT coverage for Session (gbirchmeier)
### v1.13.0
diff --git a/UnitTests/SessionTest.cs b/UnitTests/SessionTest.cs
index 5bc9cf810..771a04b8b 100755
--- a/UnitTests/SessionTest.cs
+++ b/UnitTests/SessionTest.cs
@@ -170,7 +170,7 @@ public bool SENT_REJECT(int reason, int refTag)
return refTagField.Value == refTag;
}
- public void SendNOSMessage()
+ public QuickFix.FIX42.NewOrderSingle CreateNOSMessage(SeqNumType n)
{
QuickFix.FIX42.NewOrderSingle order = new QuickFix.FIX42.NewOrderSingle(
new QuickFix.Fields.ClOrdID("1"),
@@ -182,9 +182,19 @@ public void SendNOSMessage()
order.Header.SetField(new QuickFix.Fields.TargetCompID(_sessionId.SenderCompID));
order.Header.SetField(new QuickFix.Fields.SenderCompID(_sessionId.TargetCompID));
- order.Header.SetField(new QuickFix.Fields.MsgSeqNum(_seqNum++));
+ order.Header.SetField(new QuickFix.Fields.SendingTime(DateTime.UtcNow));
+ order.Header.SetField(new QuickFix.Fields.MsgSeqNum(n));
+ return order;
+ }
+
+ public void SendNOSMessage()
+ {
+ _session!.Next(CreateNOSMessage(_seqNum++).ConstructString());
+ }
- _session!.Next(order.ConstructString());
+ public void SendNOSMessage(SeqNumType n)
+ {
+ _session!.Next(CreateNOSMessage(n).ConstructString());
}
public void SendResendRequest(SeqNumType begin, SeqNumType end)
@@ -824,5 +834,112 @@ public void TestResendRequestMsgSeqNumNotIgnoredWhenNoPersistance()
SendNOSMessage();
Assert.That(!SENT_RESEND_REQUEST());
}
+
+ [Test]
+ public void TestGenerateResendRequest() {
+ _session!.NextTargetMsgSeqNum = 100;
+
+ // <= FIX41
+ _session.GenerateResendRequest(QuickFix.FixValues.BeginString.FIX41, 125);
+ Assert.That(_responder.GetCount(QuickFix.Fields.MsgType.RESEND_REQUEST), Is.EqualTo(1));
+ var rr = _responder.MsgLookup[QuickFix.Fields.MsgType.RESEND_REQUEST].Dequeue();
+ Assert.That(rr.GetInt(QuickFix.Fields.Tags.BeginSeqNo), Is.EqualTo(100));
+ Assert.That(rr.GetInt(QuickFix.Fields.Tags.EndSeqNo), Is.EqualTo(999999));
+
+ // >= FIX42
+ _session.GenerateResendRequest(QuickFix.FixValues.BeginString.FIX42, 125);
+ Assert.That(_responder.GetCount(QuickFix.Fields.MsgType.RESEND_REQUEST), Is.EqualTo(1));
+ rr = _responder.MsgLookup[QuickFix.Fields.MsgType.RESEND_REQUEST].Dequeue();
+ Assert.That(rr.GetInt(QuickFix.Fields.Tags.BeginSeqNo), Is.EqualTo(100));
+ Assert.That(rr.GetInt(QuickFix.Fields.Tags.EndSeqNo), Is.EqualTo(0));
+
+ // Max resend is set: request is greater than max resend
+ _session.MaxMessagesInResendRequest = 100;
+ _session.GenerateResendRequest(QuickFix.FixValues.BeginString.FIX42, 225);
+ Assert.That(_responder.GetCount(QuickFix.Fields.MsgType.RESEND_REQUEST), Is.EqualTo(1));
+ rr = _responder.MsgLookup[QuickFix.Fields.MsgType.RESEND_REQUEST].Dequeue();
+ Assert.That(rr.GetInt(QuickFix.Fields.Tags.BeginSeqNo), Is.EqualTo(100));
+ Assert.That(rr.GetInt(QuickFix.Fields.Tags.EndSeqNo), Is.EqualTo(199));
+
+ // Max resend is set: request is lesser than max resend
+ _session.GenerateResendRequest(QuickFix.FixValues.BeginString.FIX42, 175);
+ Assert.That(_responder.GetCount(QuickFix.Fields.MsgType.RESEND_REQUEST), Is.EqualTo(1));
+ rr = _responder.MsgLookup[QuickFix.Fields.MsgType.RESEND_REQUEST].Dequeue();
+ Assert.That(rr.GetInt(QuickFix.Fields.Tags.BeginSeqNo), Is.EqualTo(100));
+ Assert.That(rr.GetInt(QuickFix.Fields.Tags.EndSeqNo), Is.EqualTo(174));
+ }
+
+ [Test]
+ public void TestBasicResendRequest_NoMax() {
+ // just a regular boring resend request scenario, when MaxMessagesInResendRequest is unset
+
+ // Setup: Establish connection. Server sends Seq too high, causing client to ResendRequest.
+ Logon();
+ SendNOSMessage();
+ Assert.That(_session!.NextTargetMsgSeqNum, Is.EqualTo(3));
+
+ SendNOSMessage(6);
+ Assert.That(_responder.GetCount(QuickFix.Fields.MsgType.RESEND_REQUEST), Is.EqualTo(1));
+
+ var resendRequest =
+ (QuickFix.FIX42.ResendRequest)_responder.MsgLookup[QuickFix.Fields.MsgType.RESEND_REQUEST].Dequeue();
+ Assert.That(resendRequest.BeginSeqNo.Value, Is.EqualTo(3));
+ Assert.That(resendRequest.EndSeqNo.Value, Is.EqualTo(0)); // 0 is infinity, but actually we just want until 5
+
+ Assert.That(_session.IsResendRequested, Is.True);
+
+ // Server sends 3 resent messages and 2 new messages
+ // (client already got seq=6)
+ foreach (SeqNumType i in new[] { 3, 4, 5, 7, 8 }) {
+ var msg = CreateNOSMessage(i);
+ if (i < 7) {
+ msg.Header.SetField(new QuickFix.Fields.PossDupFlag(true));
+ msg.Header.SetField(new QuickFix.Fields.OrigSendingTime(DateTime.MinValue)); // (value doesn't matter for test)
+ _session.Next(msg.ConstructString());
+ }
+ _session.Next(msg.ConstructString());
+
+ // When EndSeqNo is 0, we mark the ResendResend as 'finished' after the first resent message is received
+ Assert.That(_session.IsResendRequested, Is.False, $"seq num {i}");
+ }
+ }
+
+ [Test]
+ public void TestBasicResendRequest_WithMax() {
+ // just a regular boring resend request scenario, when MaxMessagesInResendRequest is configured
+
+ // Setup: Establish connection. Server sends Seq too high, causing client to ResendRequest.
+ _session!.MaxMessagesInResendRequest = 100;
+ Logon();
+ SendNOSMessage();
+ Assert.That(_session!.NextTargetMsgSeqNum, Is.EqualTo(3));
+
+ SendNOSMessage(6);
+ Assert.That(_responder.GetCount(QuickFix.Fields.MsgType.RESEND_REQUEST), Is.EqualTo(1));
+
+ var resendRequest =
+ (QuickFix.FIX42.ResendRequest)_responder.MsgLookup[QuickFix.Fields.MsgType.RESEND_REQUEST].Dequeue();
+ Assert.That(resendRequest.BeginSeqNo.Value, Is.EqualTo(3));
+ Assert.That(resendRequest.EndSeqNo.Value, Is.EqualTo(5));
+
+ Assert.That(_session.IsResendRequested, Is.True);
+
+ // Server sends 3 resent messages and 2 new messages
+ // (client already got seq=6)
+ foreach (SeqNumType i in new[] { 3, 4, 5, 7, 8 }) {
+ var msg = CreateNOSMessage(i);
+ if (i < 7) {
+ msg.Header.SetField(new QuickFix.Fields.PossDupFlag(true));
+ msg.Header.SetField(new QuickFix.Fields.OrigSendingTime(DateTime.MinValue)); // (value doesn't matter for test)
+ _session.Next(msg.ConstructString());
+ }
+ _session.Next(msg.ConstructString());
+
+ if (i < 5)
+ Assert.That(_session.IsResendRequested, Is.True, $"seq num {i}");
+ else
+ Assert.That(_session.IsResendRequested, Is.False, $"seq num {i}");
+ }
+ }
}