Skip to content

Commit dc22c5d

Browse files
authored
Merge pull request #624 from mgatny/main
Preserve original body when resending
2 parents 3bf2b87 + 827728d commit dc22c5d

File tree

3 files changed

+62
-3
lines changed

3 files changed

+62
-3
lines changed

in_session.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ func (state inSession) resendMessages(session *session, beginSeqNo, endSeqNo int
253253
}
254254

255255
session.log.OnEventf("Resending Message: %v", sentMessageSeqNum)
256-
msgBytes = msg.build()
256+
msgBytes = msg.buildWithBodyBytes(msg.bodyBytes) // workaround for maintaining repeating group field order
257257
session.EnqueueBytesAndSend(msgBytes)
258258

259259
seqNum = sentMessageSeqNum + 1

message.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ func ParseMessageWithDataDictionary(
210210

211211
trailerBytes := []byte{}
212212
foundBody := false
213+
foundTrailer := false
213214
for {
214215
parsedFieldBytes = &msg.fields[fieldIndex]
215216
if xmlDataLen > 0 {
@@ -228,6 +229,7 @@ func ParseMessageWithDataDictionary(
228229
msg.Header.add(msg.fields[fieldIndex : fieldIndex+1])
229230
case isTrailerField(parsedFieldBytes.tag, transportDataDictionary):
230231
msg.Trailer.add(msg.fields[fieldIndex : fieldIndex+1])
232+
foundTrailer = true
231233
default:
232234
foundBody = true
233235
trailerBytes = rawBytes
@@ -247,6 +249,12 @@ func ParseMessageWithDataDictionary(
247249
fieldIndex++
248250
}
249251

252+
// This will happen if there are no fields in the body
253+
if foundTrailer && !foundBody {
254+
trailerBytes = rawBytes
255+
msg.bodyBytes = nil
256+
}
257+
250258
// Body length would only be larger than trailer if fields out of order.
251259
if len(msg.bodyBytes) > len(trailerBytes) {
252260
msg.bodyBytes = msg.bodyBytes[:len(msg.bodyBytes)-len(trailerBytes)]
@@ -417,6 +425,21 @@ func (m *Message) build() []byte {
417425
return b.Bytes()
418426
}
419427

428+
// Constructs a []byte from a Message instance, using the given bodyBytes.
429+
// This is a workaround for the fact that we currently rely on the generated Message types to properly serialize/deserialize RepeatingGroups.
430+
// In other words, we cannot go from bytes to a Message then back to bytes, which is exactly what we need to do in the case of a Resend.
431+
// 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.
432+
// Note: The only standard non-Body group is NoHops. If that is used in the Header, this workaround may fail.
433+
func (m *Message) buildWithBodyBytes(bodyBytes []byte) []byte {
434+
m.cook()
435+
436+
var b bytes.Buffer
437+
m.Header.write(&b)
438+
b.Write(bodyBytes)
439+
m.Trailer.write(&b)
440+
return b.Bytes()
441+
}
442+
420443
func (m *Message) cook() {
421444
bodyLength := m.Header.length() + m.Body.length() + m.Trailer.length()
422445
m.Header.SetInt(tagBodyLength, bodyLength)

message_test.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,18 +133,54 @@ func (s *MessageSuite) TestReBuild() {
133133

134134
s.msg.Header.SetField(tagOrigSendingTime, FIXString("20140515-19:49:56.659"))
135135
s.msg.Header.SetField(tagSendingTime, FIXString("20140615-19:49:56"))
136+
s.msg.Header.SetField(tagPossDupFlag, FIXBoolean(true))
136137

137138
rebuildBytes := s.msg.build()
138139

139-
expectedBytes := []byte("8=FIX.4.29=12635=D34=249=TW52=20140615-19:49:5656=ISLD122=20140515-19:49:56.65911=10021=140=154=155=TSLA60=00010101-00:00:00.00010=128")
140+
expectedBytes := []byte("8=FIX.4.29=13135=D34=243=Y49=TW52=20140615-19:49:5656=ISLD122=20140515-19:49:56.65911=10021=140=154=155=TSLA60=00010101-00:00:00.00010=122")
140141

141-
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n +%s\n-%s", rebuildBytes, expectedBytes)
142+
s.True(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n +%s\n -%s", rebuildBytes, expectedBytes)
142143

143144
expectedBodyBytes := []byte("11=10021=140=154=155=TSLA60=00010101-00:00:00.000")
144145

145146
s.True(bytes.Equal(s.msg.bodyBytes, expectedBodyBytes), "Incorrect body bytes, got %s", string(s.msg.bodyBytes))
146147
}
147148

149+
func (s *MessageSuite) TestReBuildWithRepeatingGroupForResend() {
150+
// Given the following message with a repeating group
151+
origHeader := "8=FIXT.1.19=16135=834=349=ISLD52=20240415-03:43:17.92356=TW"
152+
origBody := "6=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=1448=xyzzy447=D452=1"
153+
origTrailer := "10=014"
154+
rawMsg := bytes.NewBufferString(origHeader + origBody + origTrailer)
155+
156+
// When I reparse the message from the store during a resend request
157+
s.Nil(ParseMessage(s.msg, rawMsg))
158+
159+
// And I update the headers for resend
160+
s.msg.Header.SetField(tagOrigSendingTime, FIXString("20240415-03:43:17.923"))
161+
s.msg.Header.SetField(tagSendingTime, FIXString("20240415-14:41:23.456"))
162+
s.msg.Header.SetField(tagPossDupFlag, FIXBoolean(true))
163+
164+
// When I rebuild the message
165+
rebuildBytes := s.msg.build()
166+
167+
// Then the repeating groups will not be in the correct order in the rebuilt message (note tags 447, 448, 452, 453)
168+
expectedBytes := []byte("8=FIXT.1.19=19235=834=343=Y49=ISLD52=20240415-14:41:23.45656=TW122=20240415-03:43:17.9236=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00453=1448=xyzzy447=D452=110=018")
169+
s.False(bytes.Equal(expectedBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but was: %s", expectedBytes, rebuildBytes)
170+
expectedOutOfOrderBytes := []byte("8=FIXT.1.19=19235=834=343=Y49=ISLD52=20240415-14:41:23.45656=TW122=20240415-03:43:17.9236=1.0011=114=1.0017=131=1.0032=1.0037=138=1.0039=254=155=1150=2151=0.00447=D448=xyzzy452=1453=110=018")
171+
s.True(bytes.Equal(expectedOutOfOrderBytes, rebuildBytes), "Unexpected bytes,\n expected: %s\n but was: %s", expectedOutOfOrderBytes, rebuildBytes)
172+
173+
// But the bodyBytes will still be correct
174+
origBodyBytes := []byte(origBody)
175+
s.True(bytes.Equal(origBodyBytes, s.msg.bodyBytes), "Incorrect body bytes, \n expected: %s\n but was: %s", origBodyBytes, s.msg.bodyBytes)
176+
177+
// So when I combine the updated header + the original bodyBytes + the as-is trailer
178+
resendBytes := s.msg.buildWithBodyBytes(s.msg.bodyBytes)
179+
180+
// Then the reparsed, rebuilt message will retain the correct ordering of repeating group tags during resend
181+
s.True(bytes.Equal(expectedBytes, resendBytes), "Unexpected bytes,\n expected: %s\n but was: %s", expectedBytes, resendBytes)
182+
}
183+
148184
func (s *MessageSuite) TestReverseRoute() {
149185
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")))
150186

0 commit comments

Comments
 (0)