Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(rebase #782) Proper handling of length defined data fields #832

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 47 additions & 3 deletions QuickFIXn/Message/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,39 @@ public static MsgType IdentifyType(string fixstring)
return new MsgType(GetMsgType(fixstring));
}

public static int ExtractFieldTag(string msgstr, int pos)
{
int tagend = msgstr.IndexOf('=', pos);
int tag = Convert.ToInt32(msgstr.Substring(pos, tagend - pos));
return tag;
}

public static StringField ExtractDataField(string msgstr, int dataLength, ref int pos)
{
try
{
int tagend = msgstr.IndexOf('=', pos);
int tag = Convert.ToInt32(msgstr.Substring(pos, tagend - pos));
pos = tagend + 1;
StringField field = new StringField(tag, msgstr.Substring(pos, dataLength));

pos += dataLength + 1;
return field;
}
catch (ArgumentOutOfRangeException e)
{
throw new MessageParseError($"Error at position ({pos}) while parsing msg ({msgstr})", e);
}
catch (OverflowException e)
{
throw new MessageParseError($"Error at position ({pos}) while parsing msg ({msgstr})", e);
}
catch (FormatException e)
{
throw new MessageParseError($"Error at position ({pos}) while parsing msg ({msgstr})", e);
}
}

public static StringField ExtractField(string msgstr, ref int pos)
{
try
Expand Down Expand Up @@ -314,9 +347,20 @@ public void FromString(

while (pos < msgstr.Length)
{
StringField f = ExtractField(msgstr, ref pos);

if (validate && count < 3 && Header.HEADER_FIELD_ORDER[count++] != f.Tag)
StringField? f = null;

int fieldTag = ExtractFieldTag(msgstr, pos);
if (fieldTag == Tags.XmlData)
{
if (IsHeaderField(Tags.XmlDataLen))
f = ExtractDataField(msgstr, Header.GetInt(Tags.XmlDataLen), ref pos);
else if (IsSetField(Tags.XmlDataLen))
f = ExtractDataField(msgstr, GetInt(Tags.XmlDataLen), ref pos);
}

f ??= ExtractField(msgstr, ref pos);

if (validate && (count < 3) && (Header.HEADER_FIELD_ORDER[count++] != f.Tag))
throw new InvalidMessage("Header fields out of order");

if (IsHeaderField(f.Tag, transportDict))
Expand Down
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ What's New
* #697 - new SocketIgnoreProxy setting (ABSJ415)
* #740 - Capture inner exception messages when handling authentication exceptions (rars)
* #833 - Add Try/Catch logic to SocketInitiator.OnStart() (Falcz)
* #782 - proper handling of XmlData field (larsope)

### v1.11.2:
* same as v1.11.1, but I fixed the readme in the pushed nuget packages
Expand Down
48 changes: 48 additions & 0 deletions UnitTests/MessageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class MessageTests
{
private IMessageFactory _defaultMsgFactory = new DefaultMessageFactory();

private const char Nul = Message.SOH;

[Test]
public void IdentifyTypeTest()
{
Expand Down Expand Up @@ -372,6 +374,52 @@ public void NestedRepeatingGroupParseGroupTest()
Assert.That(subGrp.GetString(Tags.PartySubID), Is.EqualTo("OHAI123"));
}

[Test]
public void ReadXmlDataTest() {
// Use tag 212/XmlDataLen to properly read 213/XmlData

QuickFix.DataDictionary.DataDictionary dd = new QuickFix.DataDictionary.DataDictionary();
dd.LoadFIXSpec("FIX42");

QuickFix.FIX42.NewOrderSingle n = new QuickFix.FIX42.NewOrderSingle();

string s = "8=FIX.4.2" + Nul + "9=495" + Nul + "35=n" + Nul + "34=31420" + Nul + "369=1003" + Nul +
"52=20200701-20:34:33.978" + Nul + "49=CME" + Nul + "50=84" +
Nul + "56=DUMMY11" + Nul + "57=SID1" + Nul + "143=US,IL" + Nul + "212=392" + Nul +
"213=<RTRF>8=FIX.4.2" + Nul + "9=356" + Nul + "35=8" + Nul + "34=36027" + Nul +
"369=18623" + Nul + "52=20200701-20:34:33.977" + Nul + "49=CME" + Nul + "50=84" + Nul +
"56=M2L000N" + Nul + "57=DUMMY" + Nul + "143=US,IL" + Nul + "1=00331" + Nul +
"6=0" + Nul + "11=ACP1593635673935" + Nul + "14=0" + Nul + "17=84618:1342652" + Nul + "20=0" +
Nul + "37=84778833500" + Nul + "38=10" + Nul + "39=0" + Nul + "40=2" + Nul +
"41=0" + Nul + "44=139.203125" + Nul + "48=204527" + Nul + "54=1" + Nul + "55=ZN" + Nul +
"59=0" + Nul + "60=20200701-20:34:33.976" + Nul + "107=ZNH1" + Nul + "150=0" + Nul +
"151=10" + Nul + "167=FUT" + Nul + "432=20200701" + Nul + "1028=Y" + Nul + "1031=Y" + Nul +
"5979=1593635673976364291" + Nul + "9717=ACP1593635673935" + Nul + "10=124" + Nul + "</RTRF>" +
Nul + "10=028" + Nul;

n.FromString(s, true, dd, dd, _defaultMsgFactory);

//verify that the data field was read correctly
Assert.AreEqual(n.Header.GetInt(212), n.Header.GetString(213).Length);
}

[Test]
public void XmlDataWithoutLengthTest() {
QuickFix.DataDictionary.DataDictionary dd = new QuickFix.DataDictionary.DataDictionary();
dd.LoadFIXSpec("FIX42");

QuickFix.FIX42.NewOrderSingle n = new QuickFix.FIX42.NewOrderSingle();

string s = "8=FIX.4.2" + Nul + "9=495" + Nul + "35=n" + Nul + "34=31420" + Nul + "369=1003" + Nul +
"52=20200701-20:34:33.978" + Nul + "49=CME" + Nul + "50=84" +
Nul + "56=DUMMY11" + Nul + "57=SID1" + Nul + "143=US,IL" + Nul +
"213=oops my length field 212 is missing" +
Nul + "10=028" + Nul;

FieldNotFoundException ex =
Assert.Throws<FieldNotFoundException>(delegate { n.FromString(s, true, dd, dd, _defaultMsgFactory); });
Assert.AreEqual("field not found for tag: 212", ex!.Message);
}


[Test]
Expand Down
Loading