diff --git a/message.go b/message.go index 35e2ff675..5bea1d375 100644 --- a/message.go +++ b/message.go @@ -588,7 +588,7 @@ func formatCheckSum(value int) string { // Build constructs a []byte from a Message instance. func (m *Message) build() []byte { - m.cook() + m.cook(m.Body.length(), m.Body.total()) var b bytes.Buffer m.Header.write(&b) @@ -603,7 +603,7 @@ func (m *Message) build() []byte { // This func lets us pull the Message from the Store, parse it, update the Header, and then build it back into bytes using the original Body. // Note: The only standard non-Body group is NoHops. If that is used in the Header, this workaround may fail. func (m *Message) buildWithBodyBytes(bodyBytes []byte) []byte { - m.cook() + m.cook(len(bodyBytes), bytesTotal(bodyBytes)) var b bytes.Buffer m.Header.write(&b) @@ -612,9 +612,9 @@ func (m *Message) buildWithBodyBytes(bodyBytes []byte) []byte { return b.Bytes() } -func (m *Message) cook() { - bodyLength := m.Header.length() + m.Body.length() + m.Trailer.length() +func (m *Message) cook(bodyLen, bodyTotal int) { + bodyLength := m.Header.length() + bodyLen + m.Trailer.length() m.Header.SetInt(tagBodyLength, bodyLength) - checkSum := (m.Header.total() + m.Body.total() + m.Trailer.total()) % 256 + checkSum := (m.Header.total() + bodyTotal + m.Trailer.total()) % 256 m.Trailer.SetString(tagCheckSum, formatCheckSum(checkSum)) } diff --git a/message_test.go b/message_test.go index b02508cd9..72f11dcd0 100644 --- a/message_test.go +++ b/message_test.go @@ -418,6 +418,36 @@ func (s *MessageSuite) TestReBuildWithRepeatingGroupForResend() { s.True(bytes.Equal(expectedBytes, resendBytes), "Unexpected bytes,\n expected: %s\n but was: %s", expectedBytes, resendBytes) } +func (s *MessageSuite) TestReBuildWithRepeatingGroupMultipleEntriesInGroupForResend() { + // Given the following message with a repeating group that has 2 entries + origHeader := "8=FIXT.1.19=18435=834=349=ISLD52=20240415-03:43:17.92356=TW" + origBody := "6=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=2448=xyzzy447=D452=1448=foobar447=D452=3" + origTrailer := "10=152" + rawMsg := bytes.NewBufferString(origHeader + origBody + origTrailer) + + // When I reparse the message from the store during a resend request + s.Nil(ParseMessage(s.msg, rawMsg)) + + // And I update the headers for resend + s.msg.Header.SetField(tagOrigSendingTime, FIXString("20240415-03:43:17.923")) + s.msg.Header.SetField(tagSendingTime, FIXString("20240415-14:41:23.456")) + s.msg.Header.SetField(tagPossDupFlag, FIXBoolean(true)) + + // The bodyBytes will still be correct + origBodyBytes := []byte(origBody) + s.True(bytes.Equal(origBodyBytes, s.msg.bodyBytes), "Incorrect body bytes, \n expected: %s\n but was: %s", origBodyBytes, s.msg.bodyBytes) + + // So when I combine the updated header + the original bodyBytes + the as-is trailer + resendBytes := s.msg.buildWithBodyBytes(origBodyBytes) + + // Then the reparsed, rebuilt message will retain the correct ordering of repeating group tags during resend + expectedResendHeader := "8=FIXT.1.19=21535=834=343=Y49=ISLD52=20240415-14:41:23.45656=TW122=20240415-03:43:17.923" + expectedResendBody := "6=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=2448=xyzzy447=D452=1448=foobar447=D452=3" + expectedResendTrailer := "10=147" + expectedResendBytes := []byte(expectedResendHeader + expectedResendBody + expectedResendTrailer) + s.True(bytes.Equal(expectedResendBytes, resendBytes), "Unexpected bytes,\n expected: %s\n but was: %s", expectedResendBytes, resendBytes) +} + func (s *MessageSuite) TestReverseRoute() { s.Nil(ParseMessage(s.msg, bytes.NewBufferString("8=FIX.4.29=17135=D34=249=TW50=KK52=20060102-15:04:0556=ISLD57=AP144=BB115=JCD116=CS128=MG129=CB142=JV143=RY145=BH11=ID21=338=10040=w54=155=INTC60=20060102-15:04:0510=123"))) diff --git a/tag_value.go b/tag_value.go index 28bba7a7a..441635c3e 100644 --- a/tag_value.go +++ b/tag_value.go @@ -86,14 +86,15 @@ func (tv TagValue) String() string { return string(tv.bytes) } -func (tv TagValue) total() int { - total := 0 - - for _, b := range []byte(tv.bytes) { +func bytesTotal(bytes []byte) (total int) { + for _, b := range bytes { total += int(b) } + return +} - return total +func (tv TagValue) total() int { + return bytesTotal(tv.bytes) } func (tv TagValue) length() int { diff --git a/tag_value_test.go b/tag_value_test.go index 2c9ccc811..138b43942 100644 --- a/tag_value_test.go +++ b/tag_value_test.go @@ -86,4 +86,5 @@ func TestTagValue_total(t *testing.T) { var tv TagValue require.Nil(t, tv.parse([]byte(stringField))) assert.Equal(t, 643, tv.total(), "Total is the summation of the ascii byte values of the field string") + assert.Equal(t, 643, bytesTotal([]byte(stringField))) }