def request_resend(self, session, start, end): """Send ResendRequest message""" state = session.state logging.info("request resend from %d to %d", start, end) mg = FixMessage() mg.set(FLD_BEGIN_SEQNO, start) mg.set(FLD_END_SEQNO, end) state.recv_state = FixState.RESEND_REQUESTED self._transmit(session, MSG_RESEND_REQUEST, mg, admin=True)
def send_testrequest(self, session): """Send FIX TestRequest message""" state = session.state if state.testrq_queue: # Do not send if pending test requests return rqid = datetime_now() rq = FixMessage() rq.set(FLD_TESTREQ_ID, rqid) state.testrq_queue.append(rqid) logging.debug("Sending test request: %s", rqid) self._transmit(session, MSG_TEST_REQUEST, rq)
def validate(self, raw_data): """Convert from raw to internal FixMessage and validate it.""" data = FixMessage.from_raw(raw_data) if not data.validate(): logging.warning("disgarding message: %s", data) return None if data[0].find(self.version) == -1: # first contains wrong version number raise exc.SessionError, (exc.S_EVERSION) return data
def send_login(self, session): """Send client login data to transport.""" state = session.state hb_secs = int(session.get_conf('heartbeat_interval', 30)) reset = session.get_conf('reset_seqno', False) reset_only_on_first = session.get_conf('reset_seqno_only_on_first', True) if reset: reset_seqno = (state.logon_count == 0 and reset_only_on_first) or \ (not reset_only_on_first) else: reset_seqno = False lg = FixMessage() lg.set(FLD_HEARTBEAT_INT, hb_secs) lg.set(FLD_ENCRYPT_METHOD, 0) if reset_seqno: lg.set(FLD_RESET_SEQNUM_FLAG, 'Y') logging.warning("Reseting sequence numbers at logon (r=%d, s=%d)", state.receive_seqno, state.send_seqno) if state.send_seqno > 1: state.send_seqno = 1 if state.receive_seqno > 1: state.receive_seqno = 1 self._transmit(session, MSG_LOGON, lg, admin=True)
def _next_header(self, session, options=None): """Provide next FIX message header.""" state = session.state header = FixMessage() header.set(FLD_SENDER_COMP_ID, session.get_conf('sender_comp_id')) header.set(FLD_TARGET_COMP_ID, session.get_conf('target_comp_id')) if options is None: header.set(FLD_MSG_SEQNUM, state.send_seqno) else: if options.get(FLD_MSG_SEQNUM, int) is None: header.set(FLD_MSG_SEQNUM, state.send_seqno) # merge options header.join(options) header.set(FLD_SENDING_TIME, utc_timestamp()) return header
def resend(self, session, start, end): """Resend message as for ResendRequest""" state = session.state logging.info("handling resend from %d to %d", start, end) state.send_state = FixState.RESEND_RECEIVED resend_mode = session.get_conf('resend_mode', 'GAPFILL') opts = FixMessage() if resend_mode == 'GAPFILL' or resend_mode == 'BUYSIDE': logging.info("sending SequeceReset-GapFill: next seqno = %d", state.send_seqno+1) opts.set(FLD_POSS_DUPFLAG, 'Y') opts.set(FLD_ORIG_SENDING_TIME, utc_timestamp()) opts.set(FLD_MSG_SEQNUM, start) mg = FixMessage() mg.set(FLD_GAPFILL_FLAG, 'Y') mg.set(FLD_NEW_SEQNO, state.send_seqno+1) self._transmit(session, MSG_SEQUENCE_RESET, mg, options=opts, admin=True) elif resend_mode == 'RESET': logging.info("sending SequeceReset-Reset") mg = FixMessage() # initialize to seqno after this message (seqno is last sent seqno) mg.set(FLD_NEW_SEQNO, state.send_seqno+1) self._transmit(session, MSG_SEQUENCE_RESET, mg, options=opts, admin=True) elif resend_mode: logging.warning("unknown resend_mode: %s", resend_mode)
def handle_admin(self, session, msgtype, seqno, data): """Handle FIX administrative messages.""" logging.debug("Administrative message %s [%d] received", msgtype, seqno) state = session.state if msgtype == MSG_REJECT: session.received(data) state.receive_seqno += 1 elif msgtype == MSG_TEST_REQUEST: test_id = data.get(FLD_TESTREQ_ID) logging.info("TestRequest received: %s", test_id) hb = FixMessage() hb.set(FLD_TESTREQ_ID, test_id) self._transmit(session, MSG_HEARTBEAT, hb, admin=True) state.receive_seqno += 1 elif msgtype == MSG_HEARTBEAT: logging.info("Heartbeat received [%d]", seqno) state.receive_seqno += 1 state.hb_count = 0 try: reqid = data.get(FLD_TESTREQ_ID) logging.debug("Response test request: %s [ql=%d]", reqid, len(state.testrq_queue)) if reqid == state.testrq_queue[0]: state.testrq_queue.pop(0) except: pass elif msgtype == MSG_RESEND_REQUEST: begin_seqno = data.get(FLD_BEGIN_SEQNO, int) end_seqno = data.get(FLD_END_SEQNO, int) logging.info("ResendRequest received [%d, %d]", begin_seqno, end_seqno) self.resend(session, begin_seqno, end_seqno) state.receive_seqno += 1 elif msgtype == MSG_SEQUENCE_RESET: logging.debug("received SequenceReset: %s", str(data)) state.receive_seqno += 1 gap_fill = data.get(FLD_GAPFILL_FLAG) new_seqno = data.get(FLD_NEW_SEQNO, int) logging.info("SequenceReset, gap_fill=%s, msg_seqno=%d, new_seqno=%d", gap_fill, seqno, new_seqno) if gap_fill == 'Y': logging.warning("GapFill: reset last received seqno to %d", new_seqno) state.receive_seqno = new_seqno else: logging.warning("Reset: reset last receive seqno to %d", new_seqno) state.receive_seqno = new_seqno elif msgtype == MSG_LOGOUT: # this propably happens mostly on server side logging.info("Logout received") session.received(data) state.receive_seqno += 1 session.close() else: logging.warning("Unknown admin message type: %s", msgtype)
def login_auth(self, session, data): """Verify login.""" session.remove_timer("logon") state = session.state msgtype = data.get(FLD_MSG_TYPE, str) seqno = data.get(FLD_MSG_SEQNUM, int) # waiting for Logon response message if msgtype == MSG_LOGOUT: msg = data.get(58) logging.error("received LOGOUT: %s", msg) raise exc.SessionError, (exc.S_EINMESSAGE, msg) elif msgtype != MSG_LOGON: logging.error("received %s message in LOGIN state", msgtype) raise exc.SessionError, (exc.S_EINMESSAGE) if seqno != state.receive_seqno: if seqno > state.receive_seqno: logging.warning('LOGON: excepted [%d] < [%d] message: missing seqnos', state.receive_seqno, seqno) state.recv_state = FixState.RESEND_REQUESTED state.send_state = FixState.NORMAL state.resend_seqno = seqno #self.request_resend(state.receive_seqno+1, 0) else: # unexpected seqno for login (message seqno < receive_seqno) logging.error('LOGON: excepted [%d] > [%d] message: failing ...', state.receive_seqno, seqno) raise exc.SessionError, (exc.S_ESEQNO) if not session.is_server: # client part when all OK #state.receive_seqno = seqno #logging.debug("set last received seqno to %d", state.receive_seqno) state.status = FixState.IN_SESSION # per FIX protocol send TestRequest after successfull Logon if state.recv_state != FixState.RESEND_REQUESTED: self.send_testrequest(session) elif session.is_server: # server part when all OK logging.debug("Creating LOGON reply ...") # reply clnt_hb = data.get(FLD_HEARTBEAT_INT, int) session.set_conf('heartbeat_interval', clnt_hb) lg = FixMessage() lg.set(FLD_HEARTBEAT_INT, clnt_hb) reset_flag = data.get(FLD_RESET_SEQNUM_FLAG) if reset_flag == 'Y': loggging.warning("Reseting sequence numbers at logon (r=%d, s=%d)", state.receive_seqno, state.send_seqno) lg.set(FLD_RESET_SEQNUM_FLAG, 'Y') state.send_seqno = 1 state.receive_seqno = 1 lg.set(FLD_ENCRYPT_METHOD, 0) state.status = FixState.IN_SESSION self._transmit(session, MSG_LOGON, lg, admin=True) # test if resend request need to be sent if state.recv_state == FixState.RESEND_REQUESTED: self.request_resend(session, state.receive_seqno, 0) else: state.receive_seqno += 1 state.logon_count += 1 state.hb_count = 0 # check if login messages forwarded... if session.get_conf("forward_logon", True): session.received(data) save_secs = int(session.get_conf('session_save_interval', -1)) if save_secs > 0: callback = functools.partial(state.save, session.get_conf('session_save_path', '')) session.add_periodic(callback, save_secs, "session_saver") elif save_secs == 0: session.state.save(session.get_conf('session_save_path')) logging.debug("end of LOGIN sequence: %s", str(state)) return True