|
53 | 53 | import java.util.ArrayList; |
54 | 54 | import java.util.Collection; |
55 | 55 | import java.util.Date; |
| 56 | +import java.util.List; |
56 | 57 | import java.util.TimeZone; |
57 | 58 | import java.util.concurrent.TimeUnit; |
58 | 59 |
|
@@ -3095,6 +3096,35 @@ public void disconnect() { |
3095 | 3096 | } |
3096 | 3097 | } |
3097 | 3098 |
|
| 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 | + |
3098 | 3128 | @Test |
3099 | 3129 | public void testSendWithAllowPosDupAsFalse_ShouldRemovePossDupFlagAndOrigSendingTime() throws Exception { |
3100 | 3130 | final UnitTestApplication application = new UnitTestApplication(); |
@@ -3161,4 +3191,52 @@ public void testSend_ShouldKeepPossDupFlagAndOrigSendingTime_GivenAllowPosDupCon |
3161 | 3191 | assertTrue(sentMessage.getHeader().isSetField(PossDupFlag.FIELD)); |
3162 | 3192 | assertTrue(sentMessage.getHeader().isSetField(OrigSendingTime.FIELD)); |
3163 | 3193 | } |
| 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 | + } |
3164 | 3242 | } |
0 commit comments