169 lines
4.7 KiB
Go
169 lines
4.7 KiB
Go
// Copyright (c) quickfixengine.org All rights reserved.
|
|
//
|
|
// This file may be distributed under the terms of the quickfixengine.org
|
|
// license as defined by quickfixengine.org and appearing in the file
|
|
// LICENSE included in the packaging of this file.
|
|
//
|
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
|
|
// THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// See http://www.quickfixengine.org/LICENSE for licensing information.
|
|
//
|
|
// Contact ask@quickfixengine.org if any conditions of this licensing
|
|
// are not clear to you.
|
|
|
|
package quickfix
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
)
|
|
|
|
var sessionsLock sync.RWMutex
|
|
var sessions = make(map[SessionID]*session)
|
|
var errDuplicateSessionID = errors.New("Duplicate SessionID")
|
|
var errUnknownSession = errors.New("Unknown session")
|
|
|
|
// Messagable is a Message or something that can be converted to a Message.
|
|
type Messagable interface {
|
|
ToMessage() *Message
|
|
}
|
|
|
|
// Send determines the session to send Messagable using header fields BeginString, TargetCompID, SenderCompID.
|
|
func Send(m Messagable) (err error) {
|
|
msg := m.ToMessage()
|
|
var beginString FIXString
|
|
if err := msg.Header.GetField(tagBeginString, &beginString); err != nil {
|
|
return err
|
|
}
|
|
|
|
var targetCompID FIXString
|
|
if err := msg.Header.GetField(tagTargetCompID, &targetCompID); err != nil {
|
|
return err
|
|
}
|
|
|
|
var senderCompID FIXString
|
|
if err := msg.Header.GetField(tagSenderCompID, &senderCompID); err != nil {
|
|
return err
|
|
}
|
|
|
|
sessionID := SessionID{BeginString: string(beginString), TargetCompID: string(targetCompID), SenderCompID: string(senderCompID)}
|
|
|
|
return SendToTarget(msg, sessionID)
|
|
}
|
|
|
|
// SendToTarget sends a message based on the sessionID. Convenient for use in FromApp since it provides a session ID for incoming messages.
|
|
func SendToTarget(m Messagable, sessionID SessionID) error {
|
|
msg := m.ToMessage()
|
|
session, ok := lookupSession(sessionID)
|
|
if !ok {
|
|
return errUnknownSession
|
|
}
|
|
|
|
return session.queueForSend(msg)
|
|
}
|
|
|
|
// ResetSession resets session's sequence numbers.
|
|
func ResetSession(sessionID SessionID) error {
|
|
session, ok := lookupSession(sessionID)
|
|
if !ok {
|
|
return errUnknownSession
|
|
}
|
|
session.log.OnEvent("Session reset")
|
|
session.State.ShutdownNow(session)
|
|
if err := session.dropAndReset(); err != nil {
|
|
session.logError(err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UnregisterSession removes a session from the set of known sessions.
|
|
func UnregisterSession(sessionID SessionID) error {
|
|
sessionsLock.Lock()
|
|
defer sessionsLock.Unlock()
|
|
|
|
if _, ok := sessions[sessionID]; ok {
|
|
delete(sessions, sessionID)
|
|
return nil
|
|
}
|
|
|
|
return errUnknownSession
|
|
}
|
|
|
|
// SetNextTargetMsgSeqNum set the next expected target message sequence number for the session matching the session id.
|
|
func SetNextTargetMsgSeqNum(sessionID SessionID, seqNum int) error {
|
|
session, ok := lookupSession(sessionID)
|
|
if !ok {
|
|
return errUnknownSession
|
|
}
|
|
return session.store.SetNextTargetMsgSeqNum(seqNum)
|
|
}
|
|
|
|
// SetNextSenderMsgSeqNum sets the next outgoing message sequence number for the session matching the session id.
|
|
func SetNextSenderMsgSeqNum(sessionID SessionID, seqNum int) error {
|
|
session, ok := lookupSession(sessionID)
|
|
if !ok {
|
|
return errUnknownSession
|
|
}
|
|
return session.store.SetNextSenderMsgSeqNum(seqNum)
|
|
}
|
|
|
|
// GetExpectedSenderNum retrieves the expected sender sequence number for the session matching the session id.
|
|
func GetExpectedSenderNum(sessionID SessionID) (int, error) {
|
|
session, ok := lookupSession(sessionID)
|
|
if !ok {
|
|
return 0, errUnknownSession
|
|
}
|
|
return session.store.NextSenderMsgSeqNum(), nil
|
|
}
|
|
|
|
// GetExpectedTargetNum retrieves the next target sequence number for the session matching the session id.
|
|
func GetExpectedTargetNum(sessionID SessionID) (int, error) {
|
|
session, ok := lookupSession(sessionID)
|
|
if !ok {
|
|
return 0, errUnknownSession
|
|
}
|
|
return session.store.NextTargetMsgSeqNum(), nil
|
|
}
|
|
|
|
// GetMessageStore returns the MessageStore interface for session matching the session id.
|
|
func GetMessageStore(sessionID SessionID) (MessageStore, error) {
|
|
session, ok := lookupSession(sessionID)
|
|
if !ok {
|
|
return nil, errUnknownSession
|
|
}
|
|
return session.store, nil
|
|
}
|
|
|
|
// GetLog returns the Log interface for session matching the session id.
|
|
func GetLog(sessionID SessionID) (Log, error) {
|
|
session, ok := lookupSession(sessionID)
|
|
if !ok {
|
|
return nil, errUnknownSession
|
|
}
|
|
return session.log, nil
|
|
}
|
|
|
|
func registerSession(s *session) error {
|
|
sessionsLock.Lock()
|
|
defer sessionsLock.Unlock()
|
|
|
|
if _, ok := sessions[s.sessionID]; ok {
|
|
return errDuplicateSessionID
|
|
}
|
|
|
|
sessions[s.sessionID] = s
|
|
return nil
|
|
}
|
|
|
|
func lookupSession(sessionID SessionID) (s *session, ok bool) {
|
|
sessionsLock.RLock()
|
|
defer sessionsLock.RUnlock()
|
|
|
|
s, ok = sessions[sessionID]
|
|
return
|
|
}
|