def testDecode(self): protocol = importlib.import_module("pyfix.FIX44") codec = Codec(protocol) inMsg = b'8=FIX.4.4\x019=817\x0135=J\x0134=953\x0149=FIX_ALAUDIT\x0156=BFUT_ALAUDIT\x0143=N\x0152=20150615-09:21:42.459\x0170=00000002664ASLO1001\x01626=2\x0110626=5\x0171=0\x0160=20150615-10:21:42\x01857=1\x0173=1\x0111=00000006321ORLO1\x0138=100.0\x01800=100.0\x01124=1\x0132=100.0\x0117=00000009758TRLO1\x0131=484.50\x0154=2\x0153=100.0\x0155=FTI\x01207=XEUE\x01454=1\x01455=EOM5\x01456=A\x01200=201506\x01541=20150619\x01461=FXXXXX\x016=484.50\x0174=2\x0175=20150615\x0178=2\x0179=TEST123\x0130009=12345\x01467=00000014901CALO1001\x019520=00000014898CALO1\x0180=33.0\x01366=484.50\x0181=0\x01153=484.50\x0110626=5\x0179=TEST124\x0130009=12345\x01467=00000014903CALO1001\x019520=00000014899CALO1\x0180=67.0\x01366=484.50\x0181=0\x01153=484.50\x0110626=5\x01453=3\x01448=TEST1\x01447=D\x01452=3\x01802=2\x01523=12345\x01803=3\x01523=TEST1\x01803=19\x01448=TEST1WA\x01447=D\x01452=38\x01802=4\x01523=Test1 Wait\x01803=10\x01523= \x01803=26\x01523=\x01803=3\x01523=TestWaCRF2\x01803=28\x01448=hagap\x01447=D\x01452=11\x01802=2\x01523=GB\x01803=25\x01523=BarCapFutures.FETService\x01803=24\x0110=033\x01' msg, remaining = codec.decode(inMsg) self.assertEqual("8=FIX.4.4|9=817|35=J|34=953|49=FIX_ALAUDIT|56=BFUT_ALAUDIT|43=N|52=20150615-09:21:42.459|70=00000002664ASLO1001|626=2|10626=5|71=0|60=20150615-10:21:42|857=1|73=1=>[11=00000006321ORLO1|38=100.0|800=100.0]|124=1=>[32=100.0|17=00000009758TRLO1|31=484.50]|54=2|53=100.0|55=FTI|207=XEUE|454=1=>[455=EOM5|456=A]|200=201506|541=20150619|461=FXXXXX|6=484.50|74=2|75=20150615|78=1=>[79=TEST123]|30009=12345|467=00000014903CALO1001|9520=00000014899CALO1|80=67.0|366=484.50|81=0|153=484.50|79=TEST124|453=3=>[448=TEST1|447=D|452=3|802=2=>[523=12345|803=3, 523=TEST1|803=19], 448=TEST1WA|447=D|452=38|802=4=>[523=Test1 Wait|803=10, 523= |803=26, 523=|803=3, 523=TestWaCRF2|803=28], 448=hagap|447=D|452=11|802=2=>[523=GB|803=25, 523=BarCapFutures.FETService|803=24]]|10=033", str(msg))
def testDecode(self): protocol = importlib.import_module("pyfix.FIX44") codec = Codec(protocol) inMsg = b'8=FIX.4.4\x019=817\x0135=J\x0134=953\x0149=FIX_ALAUDIT\x0156=BFUT_ALAUDIT\x0143=N\x0152=20150615-09:21:42.459\x0170=00000002664ASLO1001\x01626=2\x0171=0\x0160=20150615-10:21:42\x01857=1\x0173=1\x0111=00000006321ORLO1\x0138=100.0\x01800=100.0\x01124=1\x0132=100.0\x0117=00000009758TRLO1\x0131=484.50\x0154=2\x0153=100.0\x0155=FTI\x01207=XEUE\x01454=1\x01455=EOM5\x01456=A\x01200=201506\x01541=20150619\x01461=FXXXXX\x016=484.50\x0174=2\x0175=20150615\x0178=2\x0179=TEST123\x01467=00000014901CALO1001\x0180=33.0\x01366=484.50\x0181=0\x01153=484.50\x0179=TEST124\x01467=00000014903CALO1001\x0180=67.0\x01366=484.50\x0181=0\x01153=484.50\x01453=3\x01448=TEST1\x01447=D\x01452=3\x01802=2\x01523=12345\x01803=3\x01523=TEST1\x01803=19\x01448=TEST1WA\x01447=D\x01452=38\x01802=4\x01523=Test1 Wait\x01803=10\x01523= \x01803=26\x01523=\x01803=3\x01523=TestWaCRF2\x01803=28\x01448=hagap\x01447=D\x01452=11\x01802=2\x01523=GB\x01803=25\x01523=BarCapFutures.FETService\x01803=24\x0110=033\x01' msg, remaining = codec.decode(inMsg) self.assertEqual( "8=FIX.4.4|9=817|35=J|34=953|49=FIX_ALAUDIT|56=BFUT_ALAUDIT|43=N|52=20150615-09:21:42.459|70=00000002664ASLO1001|626=2|71=0|60=20150615-10:21:42|857=1|73=1=>[11=00000006321ORLO1|38=100.0|800=100.0]|124=1=>[32=100.0|17=00000009758TRLO1|31=484.50]|54=2|53=100.0|55=FTI|207=XEUE|454=1=>[455=EOM5|456=A]|200=201506|541=20150619|461=FXXXXX|6=484.50|74=2|75=20150615|78=2=>[79=TEST123|467=00000014901CALO1001|80=33.0|366=484.50|81=0|153=484.50, 79=TEST124|467=00000014903CALO1001|80=67.0|366=484.50|81=0|153=484.50]|453=3=>[448=TEST1|447=D|452=3|802=2=>[523=12345|803=3, 523=TEST1|803=19], 448=TEST1WA|447=D|452=38|802=4=>[523=Test1 Wait|803=10, 523= |803=26, 523=|803=3, 523=TestWaCRF2|803=28], 448=hagap|447=D|452=11|802=2=>[523=GB|803=25, 523=BarCapFutures.FETService|803=24]]|10=033", str(msg))
class FIXConnectionHandler(object): def __init__(self, engine, protocol, sock=None, addr=None, observer=None): self.codec = Codec(protocol) self.engine = engine self.connectionState = ConnectionState.CONNECTED self.session = None self.addr = addr self.observer = observer self.msgBuffer = b'' self.heartbeatPeriod = 30.0 self.msgHandlers = [] self.sock = sock self.heartbeatTimerRegistration = None self.expectedHeartbeatRegistration = None self.socketEvent = FileDescriptorEventRegistration(self.handle_read, sock, EventType.READ) self.engine.eventManager.registerHandler(self.socketEvent) def address(self): return self.addr def disconnect(self): self.handle_close() def _notifyMessageObservers(self, msg, direction, persistMessage=True): if persistMessage is True: self.engine.journaller.persistMsg(msg, self.session, direction) for handler in filter(lambda x: (x[1] is None or x[1] == direction) and (x[2] is None or x[2] == msg.msgType), self.msgHandlers): handler[0](self, msg) def addMessageHandler(self, handler, direction = None, msgType = None): self.msgHandlers.append((handler, direction, msgType)) def removeMessageHandler(self, handler, direction = None, msgType = None): remove = filter(lambda x: x[0] == handler and (x[1] == direction or direction is None) and (x[2] == msgType or msgType is None), self.msgHandlers) for h in remove: self.msgHandlers.remove(h) def _sendHeartbeat(self): self.sendMsg(self.codec.protocol.messages.Messages.heartbeat()) def _expectedHeartbeat(self, type, closure): logging.warning("Expected heartbeat from peer %s" % (self.expectedHeartbeatRegistration ,)) self.sendMsg(self.codec.protocol.messages.Messages.test_request()) def registerLoggedIn(self): self.heartbeatTimerRegistration = TimerEventRegistration(lambda type, closure: self._sendHeartbeat(), self.heartbeatPeriod) self.engine.eventManager.registerHandler(self.heartbeatTimerRegistration) # register timeout for 10% more than we expect self.expectedHeartbeatRegistration = TimerEventRegistration(self._expectedHeartbeat, self.heartbeatPeriod * 1.10) self.engine.eventManager.registerHandler(self.expectedHeartbeatRegistration) def registerLoggedOut(self): if self.heartbeatTimerRegistration is not None: self.engine.eventManager.unregisterHandler(self.heartbeatTimerRegistration) self.heartbeatTimerRegistration = None if self.expectedHeartbeatRegistration is not None: self.engine.eventManager.unregisterHandler(self.expectedHeartbeatRegistration) self.expectedHeartbeatRegistration = None def _handleResendRequest(self, msg): protocol = self.codec.protocol responses = [] beginSeqNo = msg[protocol.fixtags.BeginSeqNo] endSeqNo = msg[protocol.fixtags.EndSeqNo] if int(endSeqNo) == 0: endSeqNo = sys.maxsize logging.info("Received resent request from %s to %s", beginSeqNo, endSeqNo) replayMsgs = self.engine.journaller.recoverMsgs(self.session, MessageDirection.OUTBOUND, beginSeqNo, endSeqNo) gapFillBegin = int(beginSeqNo) gapFillEnd = int(beginSeqNo) for replayMsg in replayMsgs: msgSeqNum = int(replayMsg[protocol.fixtags.MsgSeqNum]) if replayMsg[protocol.fixtags.MsgType] in protocol.msgtype.sessionMessageTypes: gapFillEnd = msgSeqNum + 1 else: if self.engine.shouldResendMessage(self.session, replayMsg): if gapFillBegin < gapFillEnd: # we need to send a gap fill message gapFillMsg = FIXMessage(protocol.msgtype.SEQUENCERESET) gapFillMsg.setField(protocol.fixtags.GapFillFlag, 'Y') gapFillMsg.setField(protocol.fixtags.MsgSeqNum, gapFillBegin) gapFillMsg.setField(protocol.fixtags.NewSeqNo, str(gapFillEnd)) responses.append(gapFillMsg) # and then resent the replayMsg replayMsg.removeField(protocol.fixtags.BeginString) replayMsg.removeField(protocol.fixtags.BodyLength) replayMsg.removeField(protocol.fixtags.SendingTime) replayMsg.removeField(protocol.fixtags.SenderCompID) replayMsg.removeField(protocol.fixtags.TargetCompID) replayMsg.removeField(protocol.fixtags.CheckSum) replayMsg.setField(protocol.fixtags.PossDupFlag, "Y") responses.append(replayMsg) gapFillBegin = msgSeqNum + 1 else: gapFillEnd = msgSeqNum + 1 responses.append(replayMsg) if gapFillBegin < gapFillEnd: # we need to send a gap fill message gapFillMsg = FIXMessage(protocol.msgtype.SEQUENCERESET) gapFillMsg.setField(protocol.fixtags.GapFillFlag, 'Y') gapFillMsg.setField(protocol.fixtags.MsgSeqNum, gapFillBegin) gapFillMsg.setField(protocol.fixtags.NewSeqNo, str(gapFillEnd)) responses.append(gapFillMsg) return responses def handle_read(self, type, closure): protocol = self.codec.protocol try: msg = self.sock.recv(8192) if msg: self.msgBuffer = self.msgBuffer + msg (decodedMsg, parsedLength) = self.codec.decode(self.msgBuffer) self.msgBuffer = self.msgBuffer[parsedLength:] while decodedMsg is not None and self.connectionState != ConnectionState.DISCONNECTED: self.processMessage(decodedMsg) (decodedMsg, parsedLength) = self.codec.decode(self.msgBuffer) self.msgBuffer = self.msgBuffer[parsedLength:] if self.expectedHeartbeatRegistration is not None: self.expectedHeartbeatRegistration.reset() else: logging.debug("Connection has been closed") self.disconnect() except ConnectionError as why: logging.debug("Connection has been closed %s" % (why, )) self.disconnect() def handleSessionMessage(self, msg): return -1 def processMessage(self, decodedMsg): protocol = self.codec.protocol beginString = decodedMsg[protocol.fixtags.BeginString] if beginString != protocol.beginstring: logging.warning("FIX BeginString is incorrect (expected: %s received: %s)", (protocol.beginstring, beginString)) self.disconnect() return msgType = decodedMsg[protocol.fixtags.MsgType] try: responses = [] if msgType in protocol.msgtype.sessionMessageTypes: (recvSeqNo, responses) = self.handleSessionMessage(decodedMsg) else: recvSeqNo = decodedMsg[protocol.fixtags.MsgSeqNum] # validate the seq number (seqNoState, lastKnownSeqNo) = self.session.validateRecvSeqNo(recvSeqNo) if seqNoState is False: # We should send a resend request logging.info("Requesting resend of messages: %s to %s" % (lastKnownSeqNo, 0)) responses.append(protocol.messages.Messages.resend_request(lastKnownSeqNo, 0)) # we still need to notify if we are processing Logon message if msgType == protocol.msgtype.LOGON: self._notifyMessageObservers(decodedMsg, MessageDirection.INBOUND, False) else: self.session.setRecvSeqNo(recvSeqNo) self._notifyMessageObservers(decodedMsg, MessageDirection.INBOUND) for m in responses: self.sendMsg(m) except SessionWarning as sw: logging.warning(sw) except SessionError as se: logging.error(se) self.disconnect() except DuplicateSeqNoError: try: if decodedMsg[protocol.fixtags.PossDupFlag] == "Y": logging.debug("Received duplicate message with PossDupFlag set") except KeyError: pass finally: logging.error("Failed to process message with duplicate seq no (MsgSeqNum: %s) (and no PossDupFlag='Y') - disconnecting" % (recvSeqNo, )) self.disconnect() def handle_close(self): if self.connectionState != ConnectionState.DISCONNECTED: logging.info("Client disconnected") self.registerLoggedOut() self.sock.close() self.connectionState = ConnectionState.DISCONNECTED self.msgHandlers.clear() if self.observer is not None: self.observer.notifyDisconnect(self) self.engine.eventManager.unregisterHandler(self.socketEvent) def sendMsg(self, msg): if self.connectionState != ConnectionState.CONNECTED and self.connectionState != ConnectionState.LOGGED_IN: raise FIXException(FIXException.FIXExceptionReason.NOT_CONNECTED) encodedMsg = self.codec.encode(msg, self.session).encode('utf-8') self.sock.send(encodedMsg) if self.heartbeatTimerRegistration is not None: self.heartbeatTimerRegistration.reset() decodedMsg, junk = self.codec.decode(encodedMsg) try: self._notifyMessageObservers(decodedMsg, MessageDirection.OUTBOUND) except DuplicateSeqNoError: logging.error("We have sent a message with a duplicate seq no, failed to persist it (MsgSeqNum: %s)" % (decodedMsg[self.codec.protocol.fixtags.MsgSeqNum]))