Skip to content

Commit 24d836b

Browse files
authored
Merge pull request #758 from quickfix-j/defaultapplverid-range
Correctly handle unknown `DefaultApplVerID` on `Logon`
2 parents b18003e + 0276061 commit 24d836b

File tree

10 files changed

+91
-20
lines changed

10 files changed

+91
-20
lines changed

.github/workflows/maven.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
java-version: ${{ matrix.java }}
2929
cache: 'maven'
3030
- name: Test with Maven
31-
run: ./mvnw install -B -V -Pminimal-fix-latest -D"java.util.logging.config.file"="logging.properties" -D"http.keepAlive"="false" -D"maven.wagon.http.pool"="false" -D"maven.wagon.httpconnectionManager.ttlSeconds"="120"
31+
run: ./mvnw install -B -V -Pminimal-fix-latest -D"java.util.logging.config.file"="./quickfixj-core/src/test/resources/logging.properties" -D"http.keepAlive"="false" -D"maven.wagon.http.pool"="false" -D"maven.wagon.httpconnectionManager.ttlSeconds"="120"
3232

3333
test-windows:
3434
runs-on: ${{ matrix.os }}
@@ -55,4 +55,4 @@ jobs:
5555
java-version: ${{ matrix.java }}
5656
cache: 'maven'
5757
- name: Test with Maven on Windows
58-
run: ./mvnw.cmd install -B -V -D"maven.javadoc.skip"="true" -P"skipBundlePlugin,minimal-fix-latest" -D"java.util.logging.config.file"="logging.properties" -D"http.keepAlive"="false" -D"maven.wagon.http.pool"="false" -D"maven.wagon.httpconnectionManager.ttlSeconds"="120"
58+
run: ./mvnw.cmd install -B -V -D"maven.javadoc.skip"="true" -P"skipBundlePlugin,minimal-fix-latest" -D"java.util.logging.config.file"="./quickfixj-core/src/test/resources/logging.properties" -D"http.keepAlive"="false" -D"maven.wagon.http.pool"="false" -D"maven.wagon.httpconnectionManager.ttlSeconds"="120"

quickfixj-base/src/main/java/quickfix/FixVersions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public interface FixVersions {
3030
String BEGINSTRING_FIX44 = "FIX.4.4";
3131

3232
/*
33-
* FIX 5.0+ does not have a begin string.
33+
* FIX 5.0+ does not have a BeginString.
3434
*/
3535
String FIX50 = "FIX.5.0";
3636
String FIX50SP1 = "FIX.5.0SP1";

quickfixj-base/src/main/java/quickfix/InvalidMessage.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919

2020
package quickfix;
2121

22-
/*
23-
* An exception when a message is not valid according to the
22+
/**
23+
* Thrown when a message is not valid according to the
2424
* basic message validation or the data dictionary.
2525
*/
2626
public class InvalidMessage extends Exception {

quickfixj-base/src/main/java/quickfix/MessageUtils.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ public static Message parse(MessageFactory messageFactory, DataDictionary dataDi
101101
* @param messageFactory
102102
* @param dataDictionary
103103
* @param messageString
104+
* @param validateChecksum
104105
* @return the parsed message
105106
* @throws InvalidMessage
106107
*/
@@ -129,6 +130,10 @@ public static boolean isLogon(String message) {
129130
return isMessageType(message, MsgType.LOGON);
130131
}
131132

133+
public static boolean isLogonMsgType(String msgType) {
134+
return MsgType.LOGON.equals(msgType);
135+
}
136+
132137
private static boolean isMessageType(String message, String msgType) {
133138
try {
134139
return msgType.equals(getMessageType(message));
@@ -208,10 +213,10 @@ public static String getStringField(String messageString, int tag) {
208213
};
209214

210215
/**
211-
* Convert an ApplVerID to a "begin string"
216+
* Convert an ApplVerID to a BeginString.
212217
*
213218
* @param applVerID
214-
* @return the begin string for the specified ApplVerID.
219+
* @return the BeginString for the specified ApplVerID.
215220
* @throws QFJException if conversion fails.
216221
* @see ApplVerID
217222
*/
@@ -239,10 +244,10 @@ public static String toBeginString(ApplVerID applVerID) throws QFJException {
239244
};
240245

241246
/**
242-
* Convert a begin string to an ApplVerID
247+
* Convert a BeginString to an ApplVerID.
243248
*
244249
* @param beginString
245-
* @return the ApplVerID for the specified begin string.
250+
* @return the ApplVerID for the specified BeginString.
246251
* @throws QFJException if conversion fails.
247252
* @see FixVersions
248253
*/

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

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class MessageSessionUtils {
3636
public static Message parse(Session session, String messageString) throws InvalidMessage {
3737
final String beginString = MessageUtils.getStringField(messageString, BeginString.FIELD);
3838
final String msgType = MessageUtils.getMessageType(messageString);
39+
final boolean isLogon = MessageUtils.isLogonMsgType(msgType);
3940
final MessageFactory messageFactory = session.getMessageFactory();
4041
final DataDictionaryProvider ddProvider = session.getDataDictionaryProvider();
4142
final ApplVerID applVerID;
@@ -44,17 +45,20 @@ public static Message parse(Session session, String messageString) throws Invali
4445
final quickfix.Message message;
4546
final DataDictionary payloadDictionary;
4647

47-
if (!MessageUtils.isAdminMessage(msgType) || MessageUtils.isLogon(messageString)) {
48+
if (!MessageUtils.isAdminMessage(msgType) || isLogon) {
4849
if (FixVersions.BEGINSTRING_FIXT11.equals(beginString)) {
49-
applVerID = getApplVerID(session, messageString);
50+
applVerID = getApplVerID(session, messageString, isLogon);
5051
} else {
5152
applVerID = MessageUtils.toApplVerID(beginString);
5253
}
53-
final DataDictionary applicationDataDictionary = ddProvider == null ? null : ddProvider
54-
.getApplicationDataDictionary(applVerID);
55-
payloadDictionary = MessageUtils.isAdminMessage(msgType)
56-
? sessionDataDictionary
57-
: applicationDataDictionary;
54+
55+
if (isLogon) {
56+
payloadDictionary = sessionDataDictionary;
57+
} else { // we got an app message
58+
final DataDictionary applicationDataDictionary = ddProvider == null ? null : ddProvider
59+
.getApplicationDataDictionary(applVerID);
60+
payloadDictionary = applicationDataDictionary;
61+
}
5862
} else {
5963
applVerID = null;
6064
payloadDictionary = sessionDataDictionary;
@@ -70,7 +74,7 @@ public static Message parse(Session session, String messageString) throws Invali
7074
return message;
7175
}
7276

73-
private static ApplVerID getApplVerID(Session session, String messageString)
77+
private static ApplVerID getApplVerID(Session session, String messageString, boolean isLogon)
7478
throws InvalidMessage {
7579
ApplVerID applVerID = null;
7680

@@ -83,7 +87,7 @@ private static ApplVerID getApplVerID(Session session, String messageString)
8387
applVerID = session.getTargetDefaultApplicationVersionID();
8488
}
8589

86-
if (applVerID == null && MessageUtils.isLogon(messageString)) {
90+
if (applVerID == null && isLogon) {
8791
final String defaultApplVerIdString = MessageUtils.getStringField(messageString,
8892
DefaultApplVerID.FIELD);
8993
if (defaultApplVerIdString != null) {

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import java.util.ArrayList;
6565
import java.util.Date;
6666
import java.util.List;
67+
import java.util.Optional;
6768
import java.util.Set;
6869
import java.util.concurrent.ConcurrentHashMap;
6970
import java.util.concurrent.ConcurrentMap;
@@ -2162,6 +2163,19 @@ private void nextLogon(Message logon) throws FieldNotFound, RejectLogon, Incorre
21622163
throw new RejectLogon("Logon attempt not within session time");
21632164
}
21642165

2166+
if (sessionID.isFIXT()) {
2167+
final DataDictionary dictionary = dataDictionaryProvider
2168+
.getSessionDataDictionary(sessionID.getBeginString());
2169+
if (dictionary != null) {
2170+
Optional<String> defaultApplVerID = logon.getOptionalString(DefaultApplVerID.FIELD);
2171+
if (defaultApplVerID.isPresent()) {
2172+
if (!dictionary.isFieldValue(ApplVerID.FIELD, defaultApplVerID.get())) {
2173+
throw new RejectLogon("Invalid DefaultApplVerID=" + defaultApplVerID.get());
2174+
}
2175+
}
2176+
}
2177+
}
2178+
21652179
// QFJ-926 - reset session before accepting Logon
21662180
resetIfSessionNotCurrent(sessionID, SystemTime.currentTimeMillis());
21672181

quickfixj-core/src/test/java/quickfix/mina/acceptor/AcceptorIoHandlerTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.junit.Test;
2424
import quickfix.FixVersions;
2525
import quickfix.Message;
26+
import quickfix.MessageUtils;
2627
import quickfix.Responder;
2728
import quickfix.Session;
2829
import quickfix.SessionFactoryTestSupport;
@@ -33,6 +34,7 @@
3334
import quickfix.field.ApplVerID;
3435
import quickfix.field.DefaultApplVerID;
3536
import quickfix.field.EncryptMethod;
37+
import quickfix.field.Headline;
3638
import quickfix.field.HeartBtInt;
3739
import quickfix.field.MsgSeqNum;
3840
import quickfix.field.MsgType;
@@ -42,6 +44,7 @@
4244
import quickfix.field.Text;
4345
import quickfix.fix44.Logout;
4446
import quickfix.fixt11.Logon;
47+
import quickfix.fix50.News;
4548
import quickfix.mina.EventHandlingStrategy;
4649
import quickfix.mina.NetworkingOptions;
4750
import quickfix.mina.SessionConnector;
@@ -56,6 +59,7 @@
5659

5760
import static org.junit.Assert.assertEquals;
5861
import static org.junit.Assert.assertNull;
62+
import static org.junit.Assert.assertTrue;
5963
import static org.mockito.Mockito.mock;
6064
import static org.mockito.Mockito.verify;
6165
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -101,6 +105,42 @@ public void testFIXTLogonAndApplVerID() throws Exception {
101105
}
102106
}
103107

108+
@Test
109+
public void testFIXTLogonAndUnknownApplVerID() throws Exception {
110+
EventHandlingStrategy mockEventHandlingStrategy = mock(EventHandlingStrategy.class);
111+
IoSession mockIoSession = mock(IoSession.class);
112+
SessionSettings settings = mock(SessionSettings.class);
113+
114+
final SessionID sessionID = new SessionID(FixVersions.BEGINSTRING_FIXT11, "SENDER",
115+
"TARGET");
116+
final UnitTestApplication unitTestApplication = new UnitTestApplication();
117+
try (Session session = SessionFactoryTestSupport.createSession(sessionID, unitTestApplication, false, false, true, true, new DefaultApplVerID(DefaultApplVerID.FIX50SP2))) {
118+
when(mockIoSession.getAttribute("QF_SESSION")).thenReturn(null); // to create a new Session
119+
120+
final HashMap<SessionID, Session> acceptorSessions = new HashMap<>();
121+
acceptorSessions.put(sessionID, session);
122+
final StaticAcceptorSessionProvider sessionProvider = createSessionProvider(acceptorSessions);
123+
124+
final AcceptorIoHandler handler = new AcceptorIoHandler(sessionProvider,
125+
settings, new NetworkingOptions(new Properties()), mockEventHandlingStrategy);
126+
127+
final DefaultApplVerID defaultApplVerID = new DefaultApplVerID("33");
128+
final Logon message = new Logon(new EncryptMethod(EncryptMethod.NONE_OTHER),
129+
new HeartBtInt(30), defaultApplVerID);
130+
message.getHeader().setString(TargetCompID.FIELD, sessionID.getSenderCompID());
131+
message.getHeader().setString(SenderCompID.FIELD, sessionID.getTargetCompID());
132+
message.getHeader().setField(new SendingTime(LocalDateTime.now()));
133+
message.getHeader().setInt(MsgSeqNum.FIELD, 1);
134+
135+
handler.messageReceived(mockIoSession, message.toString());
136+
session.next(message);
137+
138+
Message lastToAdminMessage = unitTestApplication.lastToAdminMessage();
139+
assertEquals(MsgType.LOGOUT, MessageUtils.getMessageType(lastToAdminMessage.toString()));
140+
assertTrue(lastToAdminMessage.getString(Text.FIELD).contains("Invalid DefaultApplVerID=33"));
141+
}
142+
}
143+
104144
@Test
105145
public void testMessageBeforeLogon() throws Exception {
106146
IoSession mockIoSession = mock(IoSession.class);
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
handlers=java.util.logging.ConsoleHandler
2-
java.util.logging.ConsoleHandler.level=ALL
2+
java.util.logging.ConsoleHandler.level=FINE
33
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
44

5-
.level=ALL
5+
.level=FINE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Send Logout on unknown DefaultApplVerID
2+
3+
iCONNECT
4+
I8=FIXT.1.135=A34=149=TW52=<TIME>56=ISLD98=0108=301137=33
5+
# we expect Logout due to 1137=33
6+
E8=FIXT.1.19=4935=534=149=ISLD52=00000000-00:00:00.00056=TW58=Invalid DefaultApplVerID=3310=0
7+
eDISCONNECT

quickfixj-messages/quickfixj-messages-fixt11/src/main/resources/FIXT11.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@
353353
<value enum="7" description="FIX50"/>
354354
<value enum="8" description="FIX50SP1"/>
355355
<value enum="9" description="FIX50SP2"/>
356+
<value enum="10" description="FIXLatest"/>
356357
</field>
357358
<field number="1129" name="CstmApplVerID" type="STRING"/>
358359
<field number="1130" name="RefApplVerID" type="STRING"/>

0 commit comments

Comments
 (0)