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

Proper handling of length defined data fields in FIX messages #625

Closed
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
100 changes: 81 additions & 19 deletions QuickFIXn/Message/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public class Message : FieldMap

public Header Header { get; private set; }
public Trailer Trailer { get; private set; }

#endregion

#region Constructors
Expand Down Expand Up @@ -131,6 +131,40 @@ 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, DataDictionary.DataDictionary sessionDD, DataDictionary.DataDictionary appDD)
{
try
{
int tagend = msgstr.IndexOf('=', pos);
int tag = Convert.ToInt32(msgstr.Substring(pos, tagend - pos));
pos = tagend + 1;
int fieldvalend = msgstr.IndexOf((char)1, pos);
StringField field = new StringField(tag, msgstr.Substring(pos, dataLength));

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

public static StringField ExtractField(string msgstr, ref int pos, DataDictionary.DataDictionary sessionDD, DataDictionary.DataDictionary appDD)
{
try
Expand Down Expand Up @@ -228,6 +262,18 @@ public static bool IsHeaderField(int tag)
return false;
}
}

public static bool IsDataField(int tag)
{
switch (tag)
{
case Tags.XmlData:
return true;
default:
return false;
}
}

public static bool IsHeaderField(int tag, DataDictionary.DataDictionary dd)
{
if (IsHeaderField(tag))
Expand Down Expand Up @@ -342,16 +388,16 @@ public static ApplVerID GetApplVerID(string beginString)
public bool FromStringHeader(string msgstr)
{
Clear();

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

if((count < 3) && (Header.HEADER_FIELD_ORDER[count++] != f.Tag))
return false;

if(IsHeaderField(f.Tag))
this.Header.SetField(f, false);
else
Expand Down Expand Up @@ -408,12 +454,29 @@ public void FromString(string msgstr, bool validate,
bool expectingBody = true;
int count = 0;
int pos = 0;
DataDictionary.IFieldMapSpec msgMap = null;
DataDictionary.IFieldMapSpec msgMap = null;

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

StringField f;
int fieldTag = ExtractFieldTag(msgstr, pos);
if (IsDataField(fieldTag))
{
// Assume length field is 1 less
int lenFieldTag = fieldTag - 1;
// Special case for Signature which violates above assumption
if (Tags.Signature.Equals(fieldTag))
f = ExtractDataField(msgstr, Tags.SignatureLength, ref pos, sessionDD, appDD);
else if (IsHeaderField(lenFieldTag))
f = ExtractDataField(msgstr, Header.GetInt(lenFieldTag), ref pos, sessionDD, appDD);
else if (IsSetField(lenFieldTag))
f = ExtractDataField(msgstr, GetInt(lenFieldTag), ref pos, sessionDD, appDD);
else
f = ExtractField(msgstr, ref pos, sessionDD, appDD);
}
else
f = ExtractField(msgstr, ref pos, sessionDD, appDD);

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

Expand All @@ -433,7 +496,7 @@ public void FromString(string msgstr, bool validate,
{
msgMap = appDD.GetMapForMessage(msgType);
}
}
}

if (!this.Header.SetField(f, false))
this.Header.RepeatedTags.Add(f);
Expand Down Expand Up @@ -470,7 +533,7 @@ public void FromString(string msgstr, bool validate,
this.RepeatedTags.Add(f);
}


if((null != msgMap) && (msgMap.IsGroup(f.Tag)))
{
pos = SetGroup(f, msgstr, pos, this, msgMap.GetGroupSpec(f.Tag), sessionDD, appDD, msgFactory);
Expand All @@ -484,7 +547,6 @@ public void FromString(string msgstr, bool validate,
}
}


[System.Obsolete("Use the version that takes an IMessageFactory instead")]
protected int SetGroup(StringField grpNoFld, string msgstr, int pos, FieldMap fieldMap, DataDictionary.IGroupSpec dd,
DataDictionary.DataDictionary sessionDataDictionary, DataDictionary.DataDictionary appDD)
Expand Down Expand Up @@ -566,7 +628,7 @@ protected int SetGroup(
pos = SetGroup(f, msgstr, pos, grp, groupDD.GetGroupSpec(f.Tag), sessionDataDictionary, appDD, msgFactory);
}
}

return grpPos;
}

Expand Down Expand Up @@ -811,14 +873,14 @@ private static string FieldMapToXML(DataDictionary.DataDictionary dd, FieldMap f
// fields
foreach (var f in fields)
{
s.Append("<field ");
if ((dd != null) && ( dd.FieldsByTag.ContainsKey(f.Key)))
{
s.Append("name=\"" + dd.FieldsByTag[f.Key].Name + "\" ");
}
s.Append("number=\"" + f.Key.ToString() + "\">");
s.Append("<![CDATA[" + f.Value.ToString() + "]]>");
s.Append("</field>");
s.Append("<field ");
if ((dd != null) && ( dd.FieldsByTag.ContainsKey(f.Key)))
{
s.Append("name=\"" + dd.FieldsByTag[f.Key].Name + "\" ");
}
s.Append("number=\"" + f.Key.ToString() + "\">");
s.Append("<![CDATA[" + f.Value.ToString() + "]]>");
s.Append("</field>");
}
// now groups
List<int> groupTags = fields.GetGroupTags();
Expand Down
22 changes: 22 additions & 0 deletions UnitTests/DataDictionaryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,28 @@ public void CheckGroupCountTest()
Assert.Throws<QuickFix.RepeatingGroupCountMismatch>(delegate { dd.CheckGroupCount(n.NoTradingSessions, n, "D"); });
}

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

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

string nul = Message.SOH;
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 ValidateWrongType()
{
Expand Down