package main import ( "bytes" "fmt" "io" "log" "net/http" "os" "os/signal" "path" "strconv" "strings" "time" "quantex.com/qfixpt/quickfix" "quantex.com/qfixpt/quickfix/config" field "quantex.com/qfixpt/quickfix/gen/field" tag "quantex.com/qfixpt/quickfix/gen/tag" filelog "quantex.com/qfixpt/quickfix/log/file" "quantex.com/qfixpt/quickfix/store/file" "quantex.com/qfixpt/quickfix/store/mongo" ) var router *quickfix.MessageRouter = quickfix.NewMessageRouter() type EchoApplication struct { log *log.Logger OrderIds map[string]bool } func (e EchoApplication) OnCreate(sessionID quickfix.SessionID) { e.log.Printf("OnCreate %v\n", sessionID.String()) } func (e *EchoApplication) OnLogon(sessionID quickfix.SessionID) { e.log.Printf("OnLogon %v\n", sessionID.String()) e.OrderIds = make(map[string]bool) } func (e *EchoApplication) OnLogout(sessionID quickfix.SessionID) { e.log.Printf("OnLogout %v\n", sessionID.String()) } func (e EchoApplication) ToAdmin(msgBuilder *quickfix.Message, sessionID quickfix.SessionID) { } func (e EchoApplication) ToApp(msgBuilder *quickfix.Message, sessionID quickfix.SessionID) (err error) { return } func (e EchoApplication) FromAdmin(msg *quickfix.Message, sessionID quickfix.SessionID) (err quickfix.MessageRejectError) { return } func (e *EchoApplication) FromApp(msg *quickfix.Message, sessionID quickfix.SessionID) (err quickfix.MessageRejectError) { e.log.Println("Got Message ", msg) return router.Route(msg, sessionID) } func (e *EchoApplication) processMsg(msg *quickfix.Message, sessionID quickfix.SessionID) quickfix.MessageRejectError { var possResend field.PossResendField msg.Header.Get(&possResend) if msg.Body.Has(tag.ClOrdID) { var orderID field.ClOrdIDField if err := msg.Body.Get(&orderID); err != nil { return err } sessionOrderID := sessionID.String() + orderID.String() if bytes.Equal(possResend.Write(), []byte("Y")) { if e.OrderIds[sessionOrderID] { return nil } } e.OrderIds[sessionOrderID] = true } reply := copyMessage(msg) if bytes.Equal(possResend.Write(), []byte("Y")) { reply.Header.Set(possResend) } quickfix.SendToTarget(reply, sessionID) return nil } func copyMessage(msg *quickfix.Message) *quickfix.Message { msgType := new(field.MsgTypeField) msg.Header.Get(msgType) msg.Header.Clear() msg.Trailer.Clear() msg.Header.Set(msgType) return msg } func main() { app := &EchoApplication{} app.log = log.New(io.Discard, "", log.LstdFlags) //app.log = log.New(os.Stdout, "", log.LstdFlags) router.AddRoute(quickfix.BeginStringFIX40, "D", app.processMsg) router.AddRoute(quickfix.BeginStringFIX41, "D", app.processMsg) router.AddRoute(quickfix.BeginStringFIX42, "D", app.processMsg) router.AddRoute(quickfix.BeginStringFIX43, "D", app.processMsg) router.AddRoute(quickfix.BeginStringFIX44, "D", app.processMsg) router.AddRoute(quickfix.ApplVerIDFIX50, "D", app.processMsg) router.AddRoute(quickfix.ApplVerIDFIX50SP1, "D", app.processMsg) router.AddRoute(quickfix.ApplVerIDFIX50SP2, "D", app.processMsg) router.AddRoute(quickfix.BeginStringFIX42, "d", app.processMsg) router.AddRoute(quickfix.BeginStringFIX43, "d", app.processMsg) router.AddRoute(quickfix.BeginStringFIX44, "d", app.processMsg) router.AddRoute(quickfix.ApplVerIDFIX50, "d", app.processMsg) router.AddRoute(quickfix.ApplVerIDFIX50SP1, "d", app.processMsg) router.AddRoute(quickfix.ApplVerIDFIX50SP2, "d", app.processMsg) fmt.Println("starting test server") cfg, err := os.Open(os.Args[1]) if err != nil { fmt.Printf("Error opening %v, %v\n", os.Args[1], err) return } appSettings, err := quickfix.ParseSettings(cfg) if err != nil { fmt.Println("Error reading cfg:", err) return } fileLogFactory, err := filelog.NewLogFactory(appSettings) if err != nil { fmt.Println("Error creating file log factory:", err) return } //fileLogFactory := quickfix.NewScreenLogFactory() // if err != nil { // fmt.Println("Error creating file log factory:", err) // return // } storeType := os.Args[2] var acceptor *quickfix.Acceptor switch strings.ToUpper(storeType) { case "MONGO": mongoDbCxn := "mongodb://localhost:27017" mongoDatabase := "automated_testing_database" mongoReplicaSet := "replicaset" appSettings.GlobalSettings().Set(config.MongoStoreConnection, mongoDbCxn) appSettings.GlobalSettings().Set(config.MongoStoreDatabase, mongoDatabase) appSettings.GlobalSettings().Set(config.MongoStoreReplicaSet, mongoReplicaSet) appSettings.GlobalSettings().Set(config.DynamicSessions, "Y") acceptor, err = quickfix.NewAcceptor(app, mongo.NewStoreFactory(appSettings), appSettings, fileLogFactory) case "FILE": fileStoreRootPath := path.Join(os.TempDir(), fmt.Sprintf("FileStoreTestSuite-%d", os.Getpid())) fileStorePath := path.Join(fileStoreRootPath, fmt.Sprintf("%d", time.Now().UnixNano())) appSettings.GlobalSettings().Set(config.FileStorePath, fileStorePath) appSettings.GlobalSettings().Set(config.DynamicSessions, "Y") acceptor, err = quickfix.NewAcceptor(app, file.NewStoreFactory(appSettings), appSettings, fileLogFactory) case "MEMORY": fallthrough default: acceptor, err = quickfix.NewAcceptor(app, quickfix.NewMemoryStoreFactory(), appSettings, fileLogFactory) } if err != nil { fmt.Println("Unable to create Acceptor: ", err) return } if err = acceptor.Start(); err != nil { fmt.Println("Unable to start Acceptor: ", err) return } // http handler for seqnum resets for specific test defs. not applicable for most tests. http.HandleFunc("/seqnum", func(w http.ResponseWriter, r *http.Request) { defer w.WriteHeader(http.StatusOK) queryParams := r.URL.Query() sessionStr, ok := queryParams["SESSION"] if !ok { fmt.Println("cannot find session") os.Exit(1) } splts := strings.Split(sessionStr[0], ":") parties := strings.Split(splts[1], "->") sessionID := quickfix.SessionID{ BeginString: splts[0], SenderCompID: parties[0], TargetCompID: parties[1], } num, ok := queryParams["NEXTTARGETSEQNUM"] if ok { seqnumInt, cErr := strconv.Atoi(num[0]) if cErr != nil { fmt.Println("cannot find seqnum") os.Exit(1) } setErr := quickfix.SetNextTargetMsgSeqNum(sessionID, seqnumInt) if setErr != nil { fmt.Println("err setting target seqnum: " + setErr.Error()) return } fmt.Println("set target seqnum to: " + num[0]) return } num, ok = queryParams["NEXTSENDERSEQNUM"] if ok { seqnumInt, cErr := strconv.Atoi(num[0]) if cErr != nil { fmt.Println("cannot find seqnum") os.Exit(1) } setErr := quickfix.SetNextSenderMsgSeqNum(sessionID, seqnumInt) if setErr != nil { fmt.Println("err setting sender seqnum: " + setErr.Error()) return } fmt.Println("set sender seqnum to: " + num[0]) return } fmt.Println("never found seqnum") os.Exit(1) }) websrvr := http.Server{Addr: ":8095"} defer func() { if err := websrvr.Close(); err != nil { fmt.Println("unable to stop srver: ", err) } }() go func() { if err := websrvr.ListenAndServe(); err != http.ErrServerClosed { fmt.Println("websrvr err: ", err) } }() c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) <-c fmt.Println("stopping test server") acceptor.Stop() }