Skip to content

Commit 9c57408

Browse files
Merge pull request #743 from michaelwilner/resends-need-to-honor-repeating-groups
A resent message with repeating groups (that does not have a data dictionary on the session) will create an incorrect length and checksum
2 parents 2497d57 + 08674a0 commit 9c57408

4 files changed

Lines changed: 42 additions & 10 deletions

File tree

message.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ func formatCheckSum(value int) string {
588588

589589
// Build constructs a []byte from a Message instance.
590590
func (m *Message) build() []byte {
591-
m.cook()
591+
m.cook(m.Body.length(), m.Body.total())
592592

593593
var b bytes.Buffer
594594
m.Header.write(&b)
@@ -603,7 +603,7 @@ func (m *Message) build() []byte {
603603
// 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.
604604
// Note: The only standard non-Body group is NoHops. If that is used in the Header, this workaround may fail.
605605
func (m *Message) buildWithBodyBytes(bodyBytes []byte) []byte {
606-
m.cook()
606+
m.cook(len(bodyBytes), bytesTotal(bodyBytes))
607607

608608
var b bytes.Buffer
609609
m.Header.write(&b)
@@ -612,9 +612,9 @@ func (m *Message) buildWithBodyBytes(bodyBytes []byte) []byte {
612612
return b.Bytes()
613613
}
614614

615-
func (m *Message) cook() {
616-
bodyLength := m.Header.length() + m.Body.length() + m.Trailer.length()
615+
func (m *Message) cook(bodyLen, bodyTotal int) {
616+
bodyLength := m.Header.length() + bodyLen + m.Trailer.length()
617617
m.Header.SetInt(tagBodyLength, bodyLength)
618-
checkSum := (m.Header.total() + m.Body.total() + m.Trailer.total()) % 256
618+
checkSum := (m.Header.total() + bodyTotal + m.Trailer.total()) % 256
619619
m.Trailer.SetString(tagCheckSum, formatCheckSum(checkSum))
620620
}

message_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,36 @@ func (s *MessageSuite) TestReBuildWithRepeatingGroupForResend() {
418418
s.True(bytes.Equal(expectedBytes, resendBytes), "Unexpected bytes,\n expected: %s\n but was: %s", expectedBytes, resendBytes)
419419
}
420420

421+
func (s *MessageSuite) TestReBuildWithRepeatingGroupMultipleEntriesInGroupForResend() {
422+
// Given the following message with a repeating group that has 2 entries
423+
origHeader := "8=FIXT.1.19=18435=834=349=ISLD52=20240415-03:43:17.92356=TW"
424+
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"
425+
origTrailer := "10=152"
426+
rawMsg := bytes.NewBufferString(origHeader + origBody + origTrailer)
427+
428+
// When I reparse the message from the store during a resend request
429+
s.Nil(ParseMessage(s.msg, rawMsg))
430+
431+
// And I update the headers for resend
432+
s.msg.Header.SetField(tagOrigSendingTime, FIXString("20240415-03:43:17.923"))
433+
s.msg.Header.SetField(tagSendingTime, FIXString("20240415-14:41:23.456"))
434+
s.msg.Header.SetField(tagPossDupFlag, FIXBoolean(true))
435+
436+
// The bodyBytes will still be correct
437+
origBodyBytes := []byte(origBody)
438+
s.True(bytes.Equal(origBodyBytes, s.msg.bodyBytes), "Incorrect body bytes, \n expected: %s\n but was: %s", origBodyBytes, s.msg.bodyBytes)
439+
440+
// So when I combine the updated header + the original bodyBytes + the as-is trailer
441+
resendBytes := s.msg.buildWithBodyBytes(origBodyBytes)
442+
443+
// Then the reparsed, rebuilt message will retain the correct ordering of repeating group tags during resend
444+
expectedResendHeader := "8=FIXT.1.19=21535=834=343=Y49=ISLD52=20240415-14:41:23.45656=TW122=20240415-03:43:17.923"
445+
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"
446+
expectedResendTrailer := "10=147"
447+
expectedResendBytes := []byte(expectedResendHeader + expectedResendBody + expectedResendTrailer)
448+
s.True(bytes.Equal(expectedResendBytes, resendBytes), "Unexpected bytes,\n expected: %s\n but was: %s", expectedResendBytes, resendBytes)
449+
}
450+
421451
func (s *MessageSuite) TestReverseRoute() {
422452
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")))
423453

tag_value.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,15 @@ func (tv TagValue) String() string {
8686
return string(tv.bytes)
8787
}
8888

89-
func (tv TagValue) total() int {
90-
total := 0
91-
92-
for _, b := range []byte(tv.bytes) {
89+
func bytesTotal(bytes []byte) (total int) {
90+
for _, b := range bytes {
9391
total += int(b)
9492
}
93+
return
94+
}
9595

96-
return total
96+
func (tv TagValue) total() int {
97+
return bytesTotal(tv.bytes)
9798
}
9899

99100
func (tv TagValue) length() int {

tag_value_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,5 @@ func TestTagValue_total(t *testing.T) {
8686
var tv TagValue
8787
require.Nil(t, tv.parse([]byte(stringField)))
8888
assert.Equal(t, 643, tv.total(), "Total is the summation of the ascii byte values of the field string")
89+
assert.Equal(t, 643, bytesTotal([]byte(stringField)))
8990
}

0 commit comments

Comments
 (0)