Skip to content
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
13 changes: 13 additions & 0 deletions config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,19 @@ const (
// - N
ValidateFieldsOutOfOrder string = "ValidateFieldsOutOfOrder"

// ValidateFieldsHaveValues if set to N, fields without values (i.e. |11=| for an empty ClOrdID)
// will not be rejected, even if RejectInvalidMessage is set to N.
// Useful for connecting to systems that improperly send empty tags.
//
// Required: No
//
// Default: Y
//
// Valid Values:
// - Y
// - N
ValidateFieldsHaveValues string = "ValidateFieldsHaveValues"

// CheckLatency if set to Y, messages must be received from the counter-party within a defined number of seconds.
// It is useful to turn this off if a system uses localtime for it's timestamps instead of GMT.
//
Expand Down
8 changes: 8 additions & 0 deletions session_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ func (f sessionFactory) newSession(
}
}

if settings.HasSetting(config.ValidateFieldsHaveValues) {
if validatorSettings.CheckFieldsHaveValues, err = settings.BoolSetting(config.ValidateFieldsHaveValues); err != nil {
return
}
}

if settings.HasSetting(config.RejectInvalidMessage) {
if validatorSettings.RejectInvalidMessage, err = settings.BoolSetting(config.RejectInvalidMessage); err != nil {
return
Expand All @@ -117,6 +123,8 @@ func (f sessionFactory) newSession(
}
}

// Always use a default message validator without data dictionaries
s.Validator = NewValidator(validatorSettings, nil, nil)
if sessionID.IsFIXT() {
if s.DefaultApplVerID, err = settings.Setting(config.DefaultApplVerID); err != nil {
return
Expand Down
54 changes: 31 additions & 23 deletions validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ type ValidatorSettings struct {
RejectInvalidMessage bool
AllowUnknownMessageFields bool
CheckUserDefinedFields bool
CheckFieldsHaveValues bool
}

// Default configuration for message validation.
// See http://www.quickfixengine.org/quickfix/doc/html/configuration.html.
var defaultValidatorSettings = ValidatorSettings{
CheckFieldsOutOfOrder: true,
CheckFieldsHaveValues: true,
RejectInvalidMessage: true,
AllowUnknownMessageFields: false,
CheckUserDefinedFields: true,
Expand Down Expand Up @@ -102,21 +104,21 @@ func (v *fixtValidator) Validate(msg *Message) MessageRejectError {
}

func validateFIX(d *datadictionary.DataDictionary, settings ValidatorSettings, msgType string, msg *Message) MessageRejectError {
if err := validateMsgType(d, msgType, msg); err != nil {
return err
}

if err := validateRequired(d, d, msgType, msg); err != nil {
return err
}
if d != nil {
if err := validateMsgType(d, msgType, msg); err != nil {
return err
}

if settings.CheckFieldsOutOfOrder {
if err := validateOrder(msg); err != nil {
if err := validateRequired(d, d, msgType, msg); err != nil {
return err
}
}

if settings.RejectInvalidMessage {
if err := validateFieldContent(msg, settings.CheckFieldsHaveValues, settings.CheckFieldsOutOfOrder); err != nil {
return err
}

if settings.RejectInvalidMessage && d != nil {
if err := validateFields(d, d, settings, msgType, msg); err != nil {
return err
}
Expand All @@ -130,21 +132,21 @@ func validateFIX(d *datadictionary.DataDictionary, settings ValidatorSettings, m
}

func validateFIXT(transportDD, appDD *datadictionary.DataDictionary, settings ValidatorSettings, msgType string, msg *Message) MessageRejectError {
if err := validateMsgType(appDD, msgType, msg); err != nil {
return err
}

if err := validateRequired(transportDD, appDD, msgType, msg); err != nil {
return err
}
if appDD != nil && transportDD != nil {
if err := validateMsgType(appDD, msgType, msg); err != nil {
return err
}

if settings.CheckFieldsOutOfOrder {
if err := validateOrder(msg); err != nil {
if err := validateRequired(transportDD, appDD, msgType, msg); err != nil {
return err
}
}

if settings.RejectInvalidMessage {
if err := validateFieldContent(msg, settings.CheckFieldsHaveValues, settings.CheckFieldsOutOfOrder); err != nil {
return err
}

if settings.RejectInvalidMessage && appDD != nil && transportDD != nil {
if err := validateFields(transportDD, appDD, settings, msgType, msg); err != nil {
return err
}
Expand Down Expand Up @@ -270,20 +272,26 @@ func validateVisitGroupField(fieldDef *datadictionary.FieldDef, fieldStack []Tag
return fieldStack, nil
}

func validateOrder(msg *Message) MessageRejectError {
func validateFieldContent(msg *Message, checkFieldsHaveValues, checkFieldsOutOfOrder bool) MessageRejectError {
if !checkFieldsHaveValues && !checkFieldsOutOfOrder {
return nil
}
inHeader := true
inTrailer := false
for _, field := range msg.fields {
t := field.tag
if checkFieldsHaveValues && len(field.value) == 0 {
return TagSpecifiedWithoutAValue(t)
}
switch {
case inHeader && t.IsHeader():
case inHeader && !t.IsHeader():
inHeader = false
case !inHeader && t.IsHeader():
case !inHeader && t.IsHeader() && checkFieldsOutOfOrder:
return tagSpecifiedOutOfRequiredOrder(t)
case t.IsTrailer():
inTrailer = true
case inTrailer && !t.IsTrailer():
case inTrailer && !t.IsTrailer() && checkFieldsOutOfOrder:
return tagSpecifiedOutOfRequiredOrder(t)
}
}
Expand Down
63 changes: 63 additions & 0 deletions validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ func TestValidate(t *testing.T) {
tcInvalidTagNumberTrailerFixT(),
tcTagSpecifiedWithoutAValue(),
tcTagSpecifiedWithoutAValueFixT(),
tcTagSpecifiedWithoutAValueValidateHasValues(),
tcTagSpecifiedWithoutAValueFixTValidateHasValues(),
tcTagSpecifiedWithoutAValueValidateHasValuesNoDataDictionary(),
tcInvalidMsgType(),
tcInvalidMsgTypeFixT(),
tcValueIsIncorrect(),
Expand Down Expand Up @@ -512,6 +515,47 @@ func tcTagSpecifiedWithoutAValue() validateTest {
}
}

func tcTagSpecifiedWithoutAValueValidateHasValues() validateTest {
dict, _ := datadictionary.Parse("spec/FIX40.xml")
settings := defaultValidatorSettings
settings.RejectInvalidMessage = false
settings.CheckFieldsHaveValues = true
validator := NewValidator(settings, dict, nil)
builder := createFIX40NewOrderSingle()

bogusTag := Tag(109)
builder.Body.SetField(bogusTag, FIXString(""))
msgBytes := builder.build()

return validateTest{
TestName: "Tag SpecifiedWithoutAValue",
Validator: validator,
MessageBytes: msgBytes,
ExpectedRejectReason: rejectReasonTagSpecifiedWithoutAValue,
ExpectedRefTagID: &bogusTag,
}
}

func tcTagSpecifiedWithoutAValueValidateHasValuesNoDataDictionary() validateTest {
settings := defaultValidatorSettings
settings.RejectInvalidMessage = false
settings.CheckFieldsHaveValues = true
validator := NewValidator(settings, nil, nil)
builder := createFIX40NewOrderSingle()

bogusTag := Tag(109)
builder.Body.SetField(bogusTag, FIXString(""))
msgBytes := builder.build()

return validateTest{
TestName: "Tag SpecifiedWithoutAValue",
Validator: validator,
MessageBytes: msgBytes,
ExpectedRejectReason: rejectReasonTagSpecifiedWithoutAValue,
ExpectedRefTagID: &bogusTag,
}
}

func tcTagSpecifiedWithoutAValueFixT() validateTest {
tDict, _ := datadictionary.Parse("spec/FIXT11.xml")
appDict, _ := datadictionary.Parse("spec/FIX50SP2.xml")
Expand All @@ -531,6 +575,25 @@ func tcTagSpecifiedWithoutAValueFixT() validateTest {
}
}

func tcTagSpecifiedWithoutAValueFixTValidateHasValues() validateTest {
tDict, _ := datadictionary.Parse("spec/FIXT11.xml")
appDict, _ := datadictionary.Parse("spec/FIX50SP2.xml")
validator := NewValidator(defaultValidatorSettings, appDict, tDict)
builder := createFIX50SP2NewOrderSingle()

bogusTag := Tag(109)
builder.Body.SetField(bogusTag, FIXString(""))
msgBytes := builder.build()

return validateTest{
TestName: "Tag SpecifiedWithoutAValue FIXT",
Validator: validator,
MessageBytes: msgBytes,
ExpectedRejectReason: rejectReasonTagSpecifiedWithoutAValue,
ExpectedRefTagID: &bogusTag,
}
}

func tcInvalidMsgType() validateTest {
dict, _ := datadictionary.Parse("spec/FIX40.xml")
validator := NewValidator(defaultValidatorSettings, dict, nil)
Expand Down
Loading