Skip to content

Commit f9b1929

Browse files
authored
Merge pull request #1115 from quickfix-j/copilot/abort-resend-on-fail
Abort resend when `send()` returns `false`
2 parents 2e9a5fa + 31bcb68 commit f9b1929

2 files changed

Lines changed: 84 additions & 1 deletion

File tree

quickfixj-core/src/main/java/quickfix/Session.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2402,7 +2402,12 @@ private void resendMessages(Message receivedMessage, int beginSeqNo, int endSeqN
24022402
generateSequenceReset(receivedMessage, begin, msgSeqNum);
24032403
}
24042404
getLog().onEvent("Resending message: " + msgSeqNum);
2405-
send(msg.toString());
2405+
boolean sent = send(msg.toString());
2406+
if (!sent) {
2407+
// Abort resend operation immediately - don't send any more messages
2408+
getLog().onWarnEvent("Resending messages aborted.");
2409+
return;
2410+
}
24062411
begin = 0;
24072412
appMessageJustSent = true;
24082413
} else {

quickfixj-core/src/test/java/quickfix/SessionTest.java

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import java.util.ArrayList;
5454
import java.util.Collection;
5555
import java.util.Date;
56+
import java.util.List;
5657
import java.util.TimeZone;
5758
import java.util.concurrent.TimeUnit;
5859

@@ -3095,6 +3096,35 @@ public void disconnect() {
30953096
}
30963097
}
30973098

3099+
private class FailingResponder implements Responder {
3100+
public int sendCallCount = 0;
3101+
public int maxSuccessfulSends;
3102+
public List<String> sentMessages = new ArrayList<>();
3103+
3104+
public FailingResponder(int maxSuccessfulSends) {
3105+
this.maxSuccessfulSends = maxSuccessfulSends;
3106+
}
3107+
3108+
@Override
3109+
public boolean send(String data) {
3110+
sendCallCount++;
3111+
if (sendCallCount <= maxSuccessfulSends) {
3112+
sentMessages.add(data);
3113+
return true;
3114+
}
3115+
return false;
3116+
}
3117+
3118+
@Override
3119+
public String getRemoteAddress() {
3120+
return null;
3121+
}
3122+
3123+
@Override
3124+
public void disconnect() {
3125+
}
3126+
}
3127+
30983128
@Test
30993129
public void testSendWithAllowPosDupAsFalse_ShouldRemovePossDupFlagAndOrigSendingTime() throws Exception {
31003130
final UnitTestApplication application = new UnitTestApplication();
@@ -3161,4 +3191,52 @@ public void testSend_ShouldKeepPossDupFlagAndOrigSendingTime_GivenAllowPosDupCon
31613191
assertTrue(sentMessage.getHeader().isSetField(PossDupFlag.FIELD));
31623192
assertTrue(sentMessage.getHeader().isSetField(OrigSendingTime.FIELD));
31633193
}
3194+
3195+
/**
3196+
* https://github.com/quickfix-j/quickfixj/issues/646
3197+
* Verify that resend operations abort when send() returns false.
3198+
* When a responder disconnects mid-resend, the resend operation should stop
3199+
* immediately rather than attempting to send all remaining messages.
3200+
*/
3201+
@Test
3202+
public void testResendAbortsWhenSendReturnsFalse() throws Exception {
3203+
final UnitTestApplication application = new UnitTestApplication();
3204+
final SessionID sessionID = new SessionID(FixVersions.BEGINSTRING_FIX44, "SENDER", "TARGET");
3205+
try (Session session = SessionFactoryTestSupport.createSession(sessionID, application, false, false, true, true, null)) {
3206+
// Create a responder that will succeed for first 2 sends, then fail
3207+
FailingResponder responder = new FailingResponder(2);
3208+
session.setResponder(responder);
3209+
final SessionState state = getSessionState(session);
3210+
3211+
// Logon
3212+
final Logon logon = new Logon();
3213+
setUpHeader(session.getSessionID(), logon, true, 1);
3214+
logon.setInt(HeartBtInt.FIELD, 30);
3215+
logon.setInt(EncryptMethod.FIELD, EncryptMethod.NONE_OTHER);
3216+
logon.toString(); // calculate length/checksum
3217+
session.next(logon);
3218+
3219+
// Send 5 application messages
3220+
session.send(createAppMessage(2));
3221+
session.send(createAppMessage(3));
3222+
session.send(createAppMessage(4));
3223+
session.send(createAppMessage(5));
3224+
session.send(createAppMessage(6));
3225+
3226+
// Reset the responder to simulate disconnect and reconnect
3227+
responder = new FailingResponder(2);
3228+
session.setResponder(responder);
3229+
3230+
// Request resend of messages 2-6
3231+
Message resendRequest = createResendRequest(2, 2);
3232+
resendRequest.toString(); // calculate length/checksum
3233+
processMessage(session, resendRequest);
3234+
3235+
// With the fix, only the first 2 messages should be sent successfully before aborting
3236+
// The 3rd send attempt will fail and cause the abort
3237+
// Without the fix, all 5 messages would be attempted (but 3+ would fail)
3238+
assertEquals("Should attempt 3 sends (2 succeed, 1 fails and aborts)", 3, responder.sendCallCount);
3239+
assertEquals("Only 2 messages should succeed", 2, responder.sentMessages.size());
3240+
}
3241+
}
31643242
}

0 commit comments

Comments
 (0)