llfix
Low-latency FIX engine
fix_server.h
1 /*
2 MIT License
3 
4 Copyright (c) 2026 Coreware Limited
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 */
24 #pragma once
25 
26 #include "common.h"
27 
28 #include <cassert>
29 #include <cstdint>
30 #include <cstddef>
31 #include <string>
32 #include <string_view>
33 #include <cstdio>
34 #include <vector>
35 #include <unordered_map>
36 #include <functional>
37 #include <mutex> // for std::lock_guard
38 #include <new>
39 
40 #include "core/compiler/hints_branch_predictor.h"
41 #include "core/compiler/hints_hot_code.h"
42 #include "core/compiler/unused.h"
43 #include "core/os/vdso.h"
44 #include "core/os/thread_local_storage.h"
45 
46 #include "core/utilities/configuration.h"
47 #include "core/utilities/logger.h"
48 #include "core/utilities/object_cache.h"
49 #include "core/utilities/std_string_utilities.h"
50 #include "core/utilities/userspace_spinlock.h"
51 
52 #include "electronic_trading/common/message_persist_plugin.h"
53 #include "electronic_trading/managed_instance/modifying_admin_command.h"
54 #include "electronic_trading/managed_instance/managed_instance.h"
55 
56 #include "fix_constants.h"
57 #include "fix_string_view.h"
58 #include "fix_string.h"
59 #include "fix_session_settings.h"
60 #include "fix_utilities.h"
61 #include "incoming_fix_message.h"
62 #include "outgoing_fix_message.h"
63 #include "fix_parser_error_codes.h"
64 #include "fix_session.h"
65 #include "fix_server_settings.h"
66 
67 #include "core/utilities/tcp_reactor_options.h"
68 
69 namespace llfix
70 {
71 
72 // Thread local
73 struct UnknownSessionContext
74 {
75  IncomingFixMessage m_incoming_fix_message;
76  ObjectCache<FixStringView> m_fix_string_view_cache;
77 };
78 
79 class FixServerConnectors
80 {
81  public:
82  FixServerConnectors()
83  {
84  m_tables_lock.initialise();
85  m_peer_index_session_table.reserve(1024);
86  m_session_peer_index_table.reserve(1024);
87  }
88 
89  bool has_peer(std::size_t peer_index) const
90  {
91  const std::lock_guard<UserspaceSpinlock<>> lock(m_tables_lock);
92 
93  if (m_peer_index_session_table.find(peer_index) == m_peer_index_session_table.end())
94  {
95  return false;
96  }
97 
98  return true;
99  }
100 
101  void update(std::size_t peer_index, FixSession* session)
102  {
103  const std::lock_guard<UserspaceSpinlock<>> lock(m_tables_lock);
104 
105  // In concurrent scenarios, disconnection for an existing item can happen before its connection as they will happen on different threads
106  if (m_session_peer_index_table.find(session) != m_session_peer_index_table.end())
107  {
108  auto existing_peer_index = m_session_peer_index_table[session];
109 
110  if (existing_peer_index != peer_index)
111  {
112  m_peer_index_session_table.erase(existing_peer_index);
113  }
114  }
115 
116  m_peer_index_session_table[peer_index] = session;
117  m_session_peer_index_table[session] = peer_index;
118  }
119 
120  FixSession* get_session(std::size_t peer_index)
121  {
122  const std::lock_guard<UserspaceSpinlock<>> lock(m_tables_lock);
123 
124  if (m_peer_index_session_table.find(peer_index) == m_peer_index_session_table.end())
125  {
126  return nullptr;
127  }
128 
129  return m_peer_index_session_table[peer_index];
130  }
131 
132  std::size_t get_peer_index(FixSession* session)
133  {
134  const std::lock_guard<UserspaceSpinlock<>> lock(m_tables_lock);
135 
136  if (m_session_peer_index_table.find(session) == m_session_peer_index_table.end())
137  {
138  return static_cast<std::size_t>(-1);
139  }
140 
141  return m_session_peer_index_table[session];
142  }
143 
144  void remove(std::size_t peer_index, FixSession* session)
145  {
146  const std::lock_guard<UserspaceSpinlock<>> lock(m_tables_lock);
147 
148  m_peer_index_session_table.erase(peer_index);
149  m_session_peer_index_table.erase(session);
150  }
151 
152  private:
153  std::unordered_map<std::size_t, FixSession*> m_peer_index_session_table;
154  std::unordered_map<FixSession*, std::size_t> m_session_peer_index_table;
155  mutable UserspaceSpinlock<> m_tables_lock;
156 };
157 
167 template<typename Transport>
168 class FixServer : public Transport, public ManagedInstance
169 {
170  public:
171  FixServer() = default;
172 
173  virtual ~FixServer()
174  {
175  for (auto& entry : m_sessions)
176  {
177  delete entry.second;
178  }
179  }
180 
189  [[nodiscard]] bool create(const std::string& server_name, const std::string server_config_file_path)
190  {
191  m_name = server_name;
192 
193  if (m_settings.load_from_config_file(server_config_file_path, server_name) == false)
194  {
195  LLFIX_LOG_ERROR("Loading settings for server " + server_name + " failed : " + m_settings.config_load_error);
196  return false;
197  }
198 
199  if (m_settings.validate() == false)
200  {
201  LLFIX_LOG_ERROR("FixServerSettings for " + m_name + " validation failed : " + m_settings.validation_error);
202  return false;
203  }
204 
205  m_is_ha_primary = m_settings.starts_as_primary_instance;
206  m_unknown_session_context_lock.initialise();
207 
208  TCPReactorOptions reactor_options;
209 
210  reactor_options.m_accept_timeout_seconds = m_settings.accept_timeout_seconds;
211  reactor_options.m_async_io_timeout_nanoseconds = m_settings.async_io_timeout_nanoseconds;
212  reactor_options.m_busy_poll_microseconds = m_settings.busy_poll_microseconds;
213  reactor_options.m_cpu_core_id = m_settings.cpu_core_id;
214  reactor_options.m_worker_thread_count = m_settings.worker_thread_count;
215  reactor_options.m_send_try_count = m_settings.send_try_count;
216  reactor_options.m_disable_nagle = m_settings.disable_nagle;
217  reactor_options.m_enable_quick_ack = m_settings.quick_ack;
218  reactor_options.m_max_poll_events = m_settings.max_poll_events;
219  reactor_options.m_nic_interface_ip = m_settings.nic_address;
220  reactor_options.m_nic_interface_name = m_settings.nic_name;
221  reactor_options.m_nic_ringbuffer_rx_size = m_settings.nic_ringbuffer_rx_size;
222  reactor_options.m_nic_ringbuffer_tx_size = m_settings.nic_ringbuffer_tx_size;
223  reactor_options.m_pending_connection_queue_size = m_settings.pending_connection_queue_size;
224  reactor_options.m_port = m_settings.accept_port;
225  reactor_options.m_rx_buffer_capacity = m_settings.rx_buffer_capacity;
226  reactor_options.m_receive_size = m_settings.receive_size;
227  reactor_options.m_socket_rx_size = m_settings.socket_rx_size;
228  reactor_options.m_socket_tx_size = m_settings.socket_tx_size;
229  reactor_options.m_spin_count = m_settings.spin_count;
230  #ifdef LLFIX_ENABLE_OPENSSL
231  reactor_options.m_use_ssl = m_settings.use_ssl;
232  reactor_options.m_ssl_verify_peer = m_settings.ssl_verify_peer;
233  reactor_options.m_ssl_ca_pem_file = m_settings.ssl_ca_pem_file;
234  reactor_options.m_ssl_cert_pem_file = m_settings.ssl_certificate_pem_file;
235  reactor_options.m_ssl_private_key_pem_file = m_settings.ssl_private_key_pem_file;
236  reactor_options.m_ssl_private_key_password = m_settings.ssl_private_key_password;
237  reactor_options.m_ssl_version = m_settings.ssl_version;
238  reactor_options.m_ssl_cipher_suite = m_settings.ssl_cipher_suite;
239  reactor_options.m_ssl_crl_path = m_settings.ssl_crl_path;
240  #endif
241 
242  this->set_params(reactor_options);
243 
244  if constexpr (Transport::is_multithreaded())
245  {
246  this->register_acceptor_callback(
247  [this](std::size_t peer_index)
248  {
249  LLFIX_UNUSED(peer_index);
250  this->process_admin_commands_of_all_non_live_sessions();
251  });
252 
253  this->register_acceptor_termination_callback(
254  [this](std::size_t peer_index)
255  {
256  LLFIX_UNUSED(peer_index);
257  this->destroy_thread_local_unknown_session_context();
258  });
259 
260  this->register_worker_callback(
261  [this](std::size_t peer_index)
262  {
263  this->process_session(peer_index);
264  });
265 
266  this->register_worker_termination_callback(
267  [this](std::size_t peer_index)
268  {
269  LLFIX_UNUSED(peer_index);
270  this->destroy_thread_local_unknown_session_context();
271  });
272  }
273  else
274  {
275  this->register_callback(std::bind(&FixServer::process, this));
276  this->register_termination_callback(std::bind(&FixServer::destroy_thread_local_unknown_session_context, this));
277  }
278 
279  LLFIX_LOG_INFO(m_name + " : Loaded server config =>\n" + m_settings.to_string());
280  LLFIX_LOG_INFO("FixServer " + m_name + " creation success");
281 
282  return true;
283  }
284 
293  [[nodiscard]] bool add_session(const std::string& session_name, FixSessionSettings& session_settings)
294  {
295  return internal_add_session(session_name, session_settings);
296  }
297 
306  [[nodiscard]] bool add_session(const std::string& session_config_file_path, const std::string& session_name)
307  {
308  FixSessionSettings session_settings;
309 
310  if (session_settings.load_from_config_file(session_config_file_path, session_name) == false)
311  {
312  LLFIX_LOG_ERROR("Loading settings for session " + session_name + " failed : " + session_settings.config_load_error);
313  return false;
314  }
315 
316  return internal_add_session(session_name, session_settings);
317  }
318 
329  [[nodiscard]] bool add_sessions_from(const std::string& session_config_file_path)
330  {
331  Configuration config_file;
332  std::string config_load_error;
333 
334  if (config_file.load_from_file(session_config_file_path, config_load_error) == false)
335  {
336  return false;
337  }
338 
339  std::vector<std::string> session_names;
340  config_file.get_group_names(session_names);
341 
342  int added_session_count{0};
343 
344  for (const auto& session_name : session_names)
345  {
346  auto lowered_session_name = StringUtilities::to_lower(session_name);
347 
348  if (StringUtilities::contains(lowered_session_name, "session"))
349  {
350  FixSessionSettings session_settings;
351 
352  if (session_settings.load_from_config_file(session_config_file_path, session_name) == false)
353  {
354  LLFIX_LOG_ERROR("Loading settings for session " + session_name + " failed : " + session_settings.config_load_error);
355  return false;
356  }
357 
358  if (internal_add_session(session_name, session_settings) == false)
359  {
360  return false;
361  }
362 
363  added_session_count++;
364  }
365  }
366 
367  if(added_session_count == 0)
368  {
369  LLFIX_LOG_ERROR(m_name + " : Could not find any session in " + session_config_file_path + ". Make sure they have 'SESSION' in their names.");
370  return false;
371  }
372 
373  return true;
374  }
375 
381  std::size_t get_session_count() const
382  {
383  return m_sessions.size();
384  }
385 
386  bool is_instance_ha_primary() const override
387  {
388  return m_is_ha_primary;
389  }
390 
398  FixSession* get_session(const std::string& session_name) override
399  {
400  if (has_session(session_name) == false)
401  {
402  return nullptr;
403  }
404 
405  return m_sessions[session_name];
406  }
407 
415  std::string get_session_name(FixSession* session)
416  {
417  assert(session);
418 
419  for (const auto& session_entry : m_sessions)
420  {
421  if(session_entry.second == session)
422  {
423  return session_entry.first;
424  }
425  }
426 
427  return "";
428  }
429 
435  void get_session_names(std::vector<std::string>& target) override
436  {
437  for (const auto& iter : m_sessions)
438  {
439  target.push_back(iter.first);
440  }
441  }
442 
443  std::string get_name() const override
444  {
445  return m_name;
446  }
447 
456  template<typename... Args>
457  void specify_repeating_group(Args... args)
458  {
459  FixSession::get_repeating_group_specs().specify_repeating_group(args...);
460  }
461 
462  #ifdef LLFIX_ENABLE_BINARY_FIELDS
463 
472  void specify_binary_field(const std::string& message_type, uint32_t tag_length, uint32_t tag_data)
473  {
474  FixSession::get_binary_field_specs().specify_binary_field(message_type, tag_length, tag_data);
475  }
476  #endif
477 
486  {
487  return session->get_outgoing_fix_message();
488  }
489 
498  virtual bool send_outgoing_message(FixSession* session, OutgoingFixMessage* message)
499  {
500  std::size_t encoded_length = 0;
501  auto int_sequence_no = session->get_sequence_store()->get_outgoing_seq_no() + 1;
502 
503  message->encode(session->get_tx_encode_buffer(), m_settings.tx_encode_buffer_capacity, int_sequence_no, encoded_length);
504  return send_bytes<true>(session, session->get_tx_encode_buffer(), encoded_length);
505  }
506 
507  void push_admin_command(const std::string& session_name, ModifyingAdminCommandType type, uint32_t arg = 0) override
508  {
509  if(llfix_likely(session_name != "*"))
510  {
511  push_admin_command_internal(session_name, type, arg);
512  }
513  else
514  {
515  for (auto& session : m_sessions)
516  {
517  push_admin_command_internal(session.first, type, arg);
518  }
519  }
520  }
521 
522  #ifdef LLFIX_AUTOMATION
523  // Used for testing outgoing msg resends and gap fills
524  bool send_fake_outgoing_message(FixSession* session, OutgoingFixMessage* message)
525  {
526  std::size_t encoded_length = 0;
527  auto int_sequence_no = session->get_sequence_store()->get_outgoing_seq_no() + 1;
528 
529  message->encode(session->get_tx_encode_buffer(), m_settings.tx_encode_buffer_capacity, int_sequence_no, encoded_length);
530 
531  auto sequence_store = session->get_sequence_store();
532  sequence_store->increment_outgoing_seq_no();
533 
534  if(session->serialisation_enabled())
535  session->get_outgoing_message_serialiser()->write(reinterpret_cast<const void*>(session->get_tx_encode_buffer()), encoded_length, true, sequence_store->get_outgoing_seq_no());
536 
537  session->set_last_sent_message_timestamp_nanoseconds(VDSO::nanoseconds_monotonic());
538 
539  return true;
540  }
541 
542  void set_replay_messages_on_incoming_resend_request_for_all_sessions(bool b)
543  {
544  for (auto& entry : m_sessions)
545  {
546  entry.second->set_replay_messages_on_incoming_resend_request(b);
547  }
548  }
549  #endif
550 
556  void shutdown() { this->stop(); }
558  // TCP connections
559  virtual void on_async_io_error(int error_code, int event_result) override
560  {
561  LLFIX_UNUSED(error_code);
562  LLFIX_UNUSED(event_result);
563  }
564 
565  virtual void on_socket_error(int error_code, int event_result) override
566  {
567  LLFIX_UNUSED(error_code);
568  LLFIX_UNUSED(event_result);
569  }
570  // Application level messages
577  virtual void on_new_order(FixSession* session, const IncomingFixMessage* message) {LLFIX_UNUSED(session); LLFIX_UNUSED(message);};
584  virtual void on_cancel_order(FixSession* session, const IncomingFixMessage* message) {LLFIX_UNUSED(session); LLFIX_UNUSED(message);};
591  virtual void on_replace_order(FixSession* session, const IncomingFixMessage* message) {LLFIX_UNUSED(session); LLFIX_UNUSED(message);};
598  virtual void on_application_level_reject(FixSession* session, const IncomingFixMessage* message) {LLFIX_UNUSED(session); LLFIX_UNUSED(message);};
605  virtual void on_session_level_reject(FixSession* session, const IncomingFixMessage* message) {LLFIX_UNUSED(session); LLFIX_UNUSED(message);};
612  virtual void on_custom_message(FixSession* session, const IncomingFixMessage* message) {LLFIX_UNUSED(session); LLFIX_UNUSED(message);};
613  // Session/admin messages
620  virtual void on_logon_request(FixSession* session, const IncomingFixMessage* message) {LLFIX_UNUSED(session); LLFIX_UNUSED(message);}
627  virtual void on_logout_request(FixSession* session, const IncomingFixMessage* message) {LLFIX_UNUSED(session); LLFIX_UNUSED(message);}
634  virtual void on_client_resend_request(FixSession* session, const IncomingFixMessage* message) {LLFIX_UNUSED(session); LLFIX_UNUSED(message);};
641  virtual void on_client_test_request(FixSession* session, const IncomingFixMessage* message) {LLFIX_UNUSED(session); LLFIX_UNUSED(message);};
647  virtual void on_client_heartbeat(FixSession* session) {LLFIX_UNUSED(session);};
648  // Others
661  virtual bool process_incoming_throttling(FixSession* session, const IncomingFixMessage* incoming_fix_message)
662  {
663  if(session->settings()->throttle_limit == 0)
664  {
665  return true;
666  }
667 
668  session->throttler()->update();
669 
670  if(session->throttler()->reached_limit() )
671  {
672  session->increment_incoming_throttler_exceed_count();
673 
674  if(session->settings()->throttle_action == IncomingThrottlerAction::WAIT)
675  {
676  session->throttler()->wait();
677  }
678  else if(session->settings()->throttle_action == IncomingThrottlerAction::DISCONNECT)
679  {
680  LLFIX_LOG_DEBUG("FixServer " + m_name + " : terminating session " + session->get_name() + " as the other end exceeded the throttle rate : " + std::to_string(session->settings()->throttle_limit));
681  this->process_connection_closure(m_fix_connectors.get_peer_index(session));
682  return false;
683  }
684  else if(session->settings()->throttle_action == IncomingThrottlerAction::REJECT)
685  {
686  send_throttle_reject_message(session, incoming_fix_message);
687  return false;
688  }
689  }
690 
691  return true;
692  }
701  virtual void process_schedule_validator(FixSession* session)
702  {
703  if (session->is_now_valid_session_datetime() == false)
704  {
705  LLFIX_LOG_DEBUG("FixServer " + m_name + " : terminating session " + session->get_name() + " due to schedule settings");
706  this->process_connection_closure(m_fix_connectors.get_peer_index(session), true);
707  }
708  }
709 
725  virtual FixSession* accept_session(std::size_t peer_index, const IncomingFixMessage* incoming_fix_message, const char* buffer, std::size_t buffer_length, uint32_t parser_reject_code)
726  {
727  if (parser_reject_code != static_cast<uint32_t>(-1))
728  {
729  std::string message;
730  if (parser_reject_code > FixConstants::FIX_MAX_ERROR_CODE)
731  {
732  message = FixParserErrorCodes::get_internal_parser_error_description(parser_reject_code) + " : " + FixUtilities::fix_to_human_readible(buffer, buffer_length);
733  }
734  else
735  {
736  char temp_buf[512];
737  std::size_t length{ 0 };
738  FixUtilities::get_reject_reason_text(temp_buf, length, parser_reject_code);
739  message = temp_buf;
740  message += " , message : " + FixUtilities::fix_to_human_readible(buffer, buffer_length);
741  }
742  process_invalid_logon(peer_index, message);
743  return nullptr;
744  }
745 
746  FixSession* session = nullptr;
747 
748  if (!(incoming_fix_message->has_tag(FixConstants::TAG_BEGIN_STRING) && incoming_fix_message->has_tag(FixConstants::TAG_MSG_SEQ_NUM) && incoming_fix_message->has_tag(FixConstants::TAG_SENDER_COMP_ID) && incoming_fix_message->has_tag(FixConstants::TAG_TARGET_COMP_ID) && incoming_fix_message->has_tag(FixConstants::TAG_MSG_TYPE)))
749  {
750  process_invalid_logon(peer_index, "One of following required tags is missing : 8,34,49,56,35 , message : " + FixUtilities::fix_to_human_readible(buffer, buffer_length));
751  return nullptr;
752  }
753  else
754  {
755  auto message_type = incoming_fix_message->get_tag_value_as<char>(FixConstants::TAG_MSG_TYPE);
756 
757  if (message_type == FixConstants::MSG_TYPE_LOGON)
758  {
759  auto incoming_begin_string = incoming_fix_message->get_tag_value_as<std::string>(FixConstants::TAG_BEGIN_STRING);
760  auto incoming_comp_id = incoming_fix_message->get_tag_value_as<std::string>(FixConstants::TAG_SENDER_COMP_ID);
761  auto incoming_target_comp_id = incoming_fix_message->get_tag_value_as<std::string>(FixConstants::TAG_TARGET_COMP_ID);
762 
763  session = find_session(incoming_begin_string, incoming_target_comp_id, incoming_comp_id);
764 
765  if (session != nullptr)
766  {
767  auto session_state = session->get_state();
768 
769  if (is_state_live(session_state)) // This check is the most important part of multithreaded FixServer
770  {
771  std::string message = "Incoming logon message for server " + m_name + " is invalid as the client is already logged on, session : " + session->get_name();
772  process_invalid_logon(peer_index, message);
773  return nullptr;
774  }
775 
776  if(session_state == SessionState::DISABLED)
777  {
778  std::string message = "Cannot accept connection attempt as session " + session->get_name() + " is disabled. Closing connection.";
779  process_invalid_logon(peer_index, message);
780  return nullptr;
781  }
782 
783  if (session->is_now_valid_session_datetime() == false)
784  {
785  std::string message = "Cannot accept connection attempt for session " + session->get_name() + " due to schedule settings. Closing connection.";
786  process_invalid_logon(peer_index, message);
787  return nullptr;
788  }
789 
791  m_fix_connectors.update(peer_index, session);
792 
793  if constexpr(Transport::is_multithreaded())
794  {
795  this->mark_connector_as_ready_for_worker_thread_dispatch(peer_index);
796  }
798  session->reset_flags();
799  session->get_incoming_fix_message()->reset();
800  session->get_incoming_fix_message()->copy_non_dirty_tag_values_from(*incoming_fix_message);
801 
802  if(incoming_fix_message->has_tag(FixConstants::TAG_RESET_SEQ_NUM_FLAG))
803  {
804  if(incoming_fix_message->get_tag_value_as<char>(FixConstants::TAG_RESET_SEQ_NUM_FLAG) == FixConstants::FIX_BOOLEAN_TRUE)
805  {
806  session->get_sequence_store()->reset_numbers();
807  }
808  }
809 
810  session->set_state(SessionState::PENDING_LOGON);
811  return session;
812  }
813  else
814  {
815  std::string message = "Could not find a session for incoming message : 8=" + incoming_begin_string + " 49=" + incoming_comp_id + " 56=" + incoming_target_comp_id + " , message : " + FixUtilities::fix_to_human_readible(buffer, buffer_length);
816  process_invalid_logon(peer_index, message);
817  return nullptr;
818  }
819  }
820  else
821  {
822  std::string message = std::string("Received a message with a msgtype different than A (") + message_type + ") while expecting a logon message : " + FixUtilities::fix_to_human_readible(buffer, buffer_length);
823  process_invalid_logon(peer_index, message);
824  return nullptr;
825  }
826  }
827  }
828 
840  virtual void process_logon_request(FixSession* session, std::size_t peer_index, const IncomingFixMessage* message)
841  {
842  if (validate_logon_request(session, peer_index, message) == false)
843  {
844  process_invalid_logon(peer_index, "");
845  return;
846  }
847 
848  if (authenticate_logon_request(session, message) == false)
849  {
850  std::string message = "Authentication failed for incoming logon message for server " + m_name + " for session " + session->get_name();
851  process_invalid_logon(peer_index, message);
852  return;
853  }
854 
855  auto requested_heartbeat_interval = message->get_tag_value_as<uint32_t>(FixConstants::TAG_HEART_BT_INT);
856  auto requested_heartbeat_interval_nanoseconds = static_cast<uint64_t>(requested_heartbeat_interval) * static_cast<uint64_t>(1'000'000'000);
857 
858  session->set_heartbeart_interval_in_nanoseconds(requested_heartbeat_interval_nanoseconds);
859  session->set_outgoing_test_request_interval_in_nanoseconds(static_cast<uint64_t>(requested_heartbeat_interval_nanoseconds * session->settings()->outgoing_test_request_interval_multiplier));
860 
861  send_logon_response(session, message);
862  session->set_state(SessionState::LOGGED_ON);
863 
864  LLFIX_LOG_DEBUG(m_name + ", session logged on : " + session->get_name() + " , peer index : " + std::to_string(peer_index));
865 
866  do_post_logon_sequence_number_check(session);
867 
868  on_logon_request(session, message);
869  }
870 
882  virtual bool authenticate_logon_request(FixSession* session, const IncomingFixMessage* message)
883  {
884  LLFIX_UNUSED(session);
885  LLFIX_UNUSED(message);
886  return true;
887  }
889  public:
890  void on_data_ready(std::size_t peer_index) override
891  {
892  auto read = this->receive(peer_index);
893 
894  if (read > 0 && read <= static_cast<int>(this->m_options.m_rx_buffer_capacity))
895  {
896  auto buffer_size = this->get_rx_buffer_size(peer_index);
897  if(buffer_size >0 && buffer_size != static_cast<std::size_t>(-1))
898  process_rx_buffer(peer_index, this->get_rx_buffer(peer_index), buffer_size);
899  }
900 
901  this->receive_done(peer_index);
902  }
903 
904  void on_client_connected(std::size_t peer_index) override
905  {
906  LLFIX_LOG_DEBUG("FixServer " + m_name + " : new client connected , peer index : " + std::to_string(peer_index));
907  }
908 
909  void on_client_disconnected(std::size_t peer_index) override
910  {
911  Transport::on_client_disconnected(peer_index);
912 
913  if (m_fix_connectors.has_peer(peer_index) == false)
914  return;
915 
916  // A logged on client
917  auto session = m_fix_connectors.get_session(peer_index);
918 
919  if(session != nullptr)
920  {
921  LLFIX_LOG_DEBUG("FixServer " + m_name + " : session " + session->get_name() + " disconnected , peer index : " + std::to_string(peer_index));
922  on_client_disconnected(session);
923  }
924 
925  // Remove entries
926  m_fix_connectors.remove(peer_index, session);
927  }
928 
929  void on_client_disconnected(FixSession* session)
930  {
931  session->set_state(SessionState::DISCONNECTED);
932  }
933 
934  FixServerSettings& get_settings() { return m_settings; }
935 
936  std::string get_settings_as_string(const std::string& delimiter) override
937  {
938  return m_settings.to_string(delimiter);
939  }
940 
946  void set_message_persist_plugin(MessagePersistPlugin* plugin)
947  {
948  assert(plugin);
949  m_message_persist_plugin = plugin;
950  }
951 
952  protected:
954  // ADMIN
963  virtual bool send_heartbeat(FixSession* session, FixString* test_request_id)
964  {
965  session->build_heartbeat_message(outgoing_message_instance(session), test_request_id);
966  return send_outgoing_message(session, outgoing_message_instance(session));
967  }
968 
977  virtual bool send_logon_response(FixSession* session, const IncomingFixMessage* message)
978  {
979  LLFIX_UNUSED(message);
980  auto response = outgoing_message_instance(session);
981  response->set_msg_type(FixConstants::MSG_TYPE_LOGON);
982 
983  // TAG 98 ENCRYPTION METHOD
984  response->set_tag(FixConstants::TAG_ENCRYPT_METHOD, 0);
985 
986  // TAG 108 HEARTBEAT INTERVAL
987  response->set_tag(FixConstants::TAG_HEART_BT_INT, static_cast<uint32_t>(session->get_heartbeart_interval_in_nanoseconds() / 1'000'000'000));
988 
989  // TAG 1137 DEFAULT APP VER ID
990  auto default_app_ver_id = session->get_default_app_ver_id();
991 
992  if (default_app_ver_id.length() > 0)
993  {
994  response->set_tag(FixConstants::TAG_DEFAULT_APPL_VER_ID, default_app_ver_id);
995  }
996 
997  return send_outgoing_message(session, response);
998  }
999 
1008  virtual bool send_logout_message(FixSession* session, const std::string& reason_text = "")
1009  {
1010  session->build_logout_message(outgoing_message_instance(session), reason_text);
1011  return send_outgoing_message(session, outgoing_message_instance(session));
1012  }
1013 
1021  virtual bool send_test_request(FixSession* session)
1022  {
1023  session->build_test_request_message(outgoing_message_instance(session));
1024  return send_outgoing_message(session, outgoing_message_instance(session));
1025  }
1026 
1034  virtual bool send_resend_request(FixSession* session)
1035  {
1036  session->build_resend_request_message(outgoing_message_instance(session), "0");
1037  return send_outgoing_message(session, outgoing_message_instance(session));
1038  }
1039 
1048  virtual bool send_sequence_reset_message(FixSession* session, uint32_t desired_sequence_no)
1049  {
1050  /*
1051  This message is a "hard" gap fill that doesn't respond to an incoming 35=2/resend request but to an internal admin request
1052  Therefore we can't set 123=Y
1053  */
1054  auto message = outgoing_message_instance(session);
1055  session->build_sequence_reset_message(message, desired_sequence_no);
1056 
1057  // ENCODE
1058  std::size_t encoded_length = 0;
1059  message->encode(session->get_tx_encode_buffer(), m_settings.tx_encode_buffer_capacity, desired_sequence_no, encoded_length);
1060 
1061  // SEND
1062  return send_bytes<false>(session, session->get_tx_encode_buffer(), encoded_length); // false is for not incrementing seq no for this message
1063  }
1064 
1072  virtual bool send_gap_fill_message(FixSession* session)
1073  {
1074  /*
1075  This message is a gap fill that responds to an incoming 35=2/resend request.
1076  For "hard" gap fills that don't set 123=Y, see send_sequence_reset_message
1077  */
1078  auto message = outgoing_message_instance(session);
1079  session->build_gap_fill_message(message);
1080 
1081  // ENCODE
1082  std::size_t encoded_length = 0;
1083  message->encode(session->get_tx_encode_buffer(), m_settings.tx_encode_buffer_capacity, session->get_incoming_resend_request_begin_no(), encoded_length); // We need to encode with seq no that the peer expects hence using m_incoming_resend_request_begin_no
1084 
1085  // SEND
1086  return send_bytes<false>(session, session->get_tx_encode_buffer(), encoded_length); // false is for not incrementing seq no for this message
1087  }
1088 
1089  virtual void resend_messages_to_client(FixSession* session, uint32_t begining_seq_no, uint32_t end_seq_no)
1090  {
1091  for (uint32_t i = begining_seq_no; i <= end_seq_no; i++)
1092  {
1093  std::size_t message_length = 0;
1094  session->get_outgoing_message_serialiser()->read_message(i, session->get_tx_encode_buffer(), m_settings.tx_encode_buffer_capacity, message_length);
1095 
1096  auto message = outgoing_message_instance(session);
1097  message->load_from_buffer(session->get_tx_encode_buffer(), message_length); // t122(orig sending time) will be handled by load_from_buffer
1098 
1099  message->template set_tag<FixMessageComponent::HEADER>(FixConstants::TAG_POSS_DUP_FLAG, FixConstants::FIX_BOOLEAN_TRUE);
1100 
1101  if (session->include_t97_during_resends())
1102  {
1103  message->template set_tag<FixMessageComponent::HEADER>(FixConstants::TAG_POSS_RESEND, FixConstants::FIX_BOOLEAN_TRUE);
1104  }
1105 
1106  std::size_t encoded_length = 0;
1107  message->encode(session->get_tx_encode_buffer(), m_settings.tx_encode_buffer_capacity, i, encoded_length);
1108  send_bytes<false>(session, session->get_tx_encode_buffer(), encoded_length); // false is for not incrementing seq no for this message
1109  }
1110  }
1111 
1112  virtual bool send_reject_message(FixSession* session, const IncomingFixMessage* incoming_message, uint32_t reject_reason_code, const char* buffer_message, std::size_t buffer_message_length, uint32_t error_tag=0)
1113  {
1114  LLFIX_UNUSED(incoming_message);
1115  char reject_reason_text[256];
1116 
1117  std::size_t text_length{ 0 };
1118  FixUtilities::get_reject_reason_text(reject_reason_text, text_length, reject_reason_code);
1119 
1120  if (error_tag == 0)
1121  {
1122  LLFIX_LOG_DEBUG("FixServer " + m_name + " session " + session->get_name() + " received an invalid message : " + std::string(reject_reason_text) + " ,message : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
1123  }
1124  else
1125  {
1126  LLFIX_LOG_DEBUG("FixServer " + m_name + " session " + session->get_name() + " received an invalid message : " + std::string(reject_reason_text) + " ,tag : " + std::to_string(error_tag) + " ,message : " + FixUtilities::fix_to_human_readible(buffer_message, buffer_message_length));
1127  }
1128 
1129  session->build_session_level_reject_message(outgoing_message_instance(session), reject_reason_code, reject_reason_text, error_tag);
1130  return send_outgoing_message(session, outgoing_message_instance(session));
1131  }
1132 
1133  virtual bool send_throttle_reject_message(FixSession* session, const IncomingFixMessage* incoming_message)
1134  {
1135  auto reject_message = outgoing_message_instance(session);
1136  reject_message->set_msg_type(FixConstants::MSG_TYPE_BUSINESS_REJECT);
1137 
1138  reject_message->set_tag(FixConstants::TAG_TEXT, session->settings()->throttler_reject_message);
1139 
1140  if (incoming_message->has_tag(FixConstants::TAG_MSG_SEQ_NUM))
1141  {
1142  reject_message->set_tag(FixConstants::TAG_REF_SEQ_NUM, incoming_message->get_tag_value_as<uint32_t>(FixConstants::TAG_MSG_SEQ_NUM));
1143  }
1144 
1145  if (incoming_message->has_tag(FixConstants::TAG_MSG_TYPE))
1146  {
1147  reject_message->set_tag(FixConstants::TAG_REF_MSG_TYPE, incoming_message->get_tag_value_as<std::string>(FixConstants::TAG_MSG_TYPE));
1148  }
1149 
1150  return send_outgoing_message(session, reject_message);
1151  }
1152 
1153  std::unordered_map<std::string, FixSession*> m_sessions;
1154 
1155  private:
1156  static inline constexpr std::size_t MINIMUM_REQUIRED_INITIAL_BUFFER_FOR_PARSING = 4; // 10=<DELIMITER>
1157  std::string m_name;
1158  bool m_is_ha_primary = true;
1159 
1160  FixServerSettings m_settings;
1161 
1162  FixServerConnectors m_fix_connectors;
1163 
1164  MessagePersistPlugin* m_message_persist_plugin = nullptr;
1165 
1166  UserspaceSpinlock<> m_unknown_session_context_lock;
1167 
1168  bool has_session(const std::string& session_name)
1169  {
1170  if (m_sessions.find(session_name) == m_sessions.end())
1171  {
1172  return false;
1173  }
1174 
1175  return true;
1176  }
1177 
1178  FixSession* find_session(const std::string& begin_string, const std::string& sender_comp_id, const std::string& target_comp_id)
1179  {
1180  FixSession* ret = nullptr;
1181 
1182  for (const auto& session_entry : m_sessions)
1183  {
1184  if (session_entry.second->settings()->sender_comp_id == sender_comp_id && session_entry.second->settings()->target_comp_id == target_comp_id && session_entry.second->settings()->begin_string == begin_string)
1185  {
1186  return session_entry.second;
1187  }
1188  }
1189 
1190  return ret;
1191  }
1192 
1193  bool does_any_session_use_path(const std::string& path)
1194  {
1195  for (const auto& session_entry : m_sessions)
1196  {
1197  if (session_entry.second->settings()->sequence_store_file_path == path)
1198  {
1199  return true;
1200  }
1201 
1202  if (session_entry.second->settings()->incoming_message_serialisation_path == path && session_entry.second->serialisation_enabled() == true)
1203  {
1204  return true;
1205  }
1206 
1207  if (session_entry.second->settings()->outgoing_message_serialisation_path == path && session_entry.second->serialisation_enabled() == true)
1208  {
1209  return true;
1210  }
1211  }
1212 
1213  return false;
1214  }
1215 
1216  bool does_any_session_use_compids(const std::string& sender_comp_id, const std::string& target_comp_id)
1217  {
1218  for (const auto& session_entry : m_sessions)
1219  {
1220  if (session_entry.second->settings()->sender_comp_id == sender_comp_id && session_entry.second->settings()->target_comp_id == target_comp_id)
1221  {
1222  return true;
1223  }
1224  }
1225  return false;
1226  }
1227 
1228  bool internal_add_session(const std::string& session_name, const FixSessionSettings& session_settings)
1229  {
1230  if (has_session(session_name))
1231  {
1232  LLFIX_LOG_ERROR(m_name + " : Already loaded a session with name " + session_name + " . You have to specify unique names for sessions.");
1233  return false;
1234  }
1235 
1236  if (does_any_session_use_path(session_settings.sequence_store_file_path))
1237  {
1238  LLFIX_LOG_ERROR(m_name + " : session " + session_name + "'s sequence_store_file_path already being used in another session.");
1239  return false;
1240  }
1241 
1242  if (does_any_session_use_path(session_settings.incoming_message_serialisation_path))
1243  {
1244  LLFIX_LOG_ERROR(m_name + " : session " + session_name + "'s incoming_message_serialisation_path already being used in another session.");
1245  return false;
1246  }
1247 
1248  if (does_any_session_use_path(session_settings.outgoing_message_serialisation_path))
1249  {
1250  LLFIX_LOG_ERROR(m_name + " : session " + session_name + "'s outgoing_message_serialisation_path already being used in another session.");
1251  return false;
1252  }
1253 
1254  if (does_any_session_use_compids(session_settings.sender_comp_id, session_settings.target_comp_id))
1255  {
1256  std::string warning_message = "WARNING (" + m_name + "): session " + session_name + "'s sender and target comp ids are already being used in another session.";
1257  LLFIX_LOG_WARNING(warning_message);
1258  fprintf(stderr, "%s", warning_message.c_str());
1259  }
1260 
1261  FixSession* current_session = new (std::nothrow) FixSession;
1262 
1263  if (current_session == nullptr)
1264  {
1265  LLFIX_LOG_ERROR(m_name + " : Failed to allocate fix session for " + session_name);
1266  return false;
1267  }
1268 
1269  session_settings.is_server = true;
1270  session_settings.tx_encode_buffer_capacity = m_settings.tx_encode_buffer_capacity; // Propagate
1271 
1272  if (current_session->initialise(session_name, session_settings) == false)
1273  {
1274  delete current_session;
1275  return false;
1276  }
1277 
1278  m_sessions[session_name] = current_session;
1279 
1280  if (m_settings.starts_as_primary_instance == false)
1281  {
1282  disable_session(current_session);
1283  }
1284 
1285  return true;
1286  }
1287 
1288  void process_session(std::size_t peer_index)
1289  {
1290  static_assert(Transport::is_multithreaded());
1291 
1292  FixSession* sess = m_fix_connectors.get_session(peer_index);
1293 
1294  if (sess)
1295  {
1296  sess->lock();
1297  process_session(sess);
1298  sess->unlock();
1299  }
1300  }
1301 
1302  void process_session(FixSession* session)
1303  {
1304  static_assert(Transport::is_multithreaded());
1305 
1306  if (llfix_likely(this->m_is_stopping.load() == false))
1307  {
1308  process_admin_commands(session);
1309 
1310  auto current_state = session->get_state();
1311 
1312  if (is_state_live(current_state))
1313  {
1314  process_outgoing_resend_request_if_necessary(session, VDSO::nanoseconds_monotonic());
1315  process_outgoing_test_request_if_necessary(session, VDSO::nanoseconds_monotonic());
1316  respond_to_resend_request_if_necessary(session);
1317  respond_to_test_request_if_necessary(session);
1318  send_heartbeat_if_necessary(session, VDSO::nanoseconds_monotonic());
1319 
1320  process_schedule_validator(session);
1321  }
1322  }
1323  else
1324  {
1325  send_logout_message(session);
1326  on_client_disconnected(session);
1327  }
1328  }
1329 
1330  void process_admin_commands_of_all_non_live_sessions()
1331  {
1332  static_assert(Transport::is_multithreaded());
1333 
1334  for (auto& iter : m_sessions)
1335  {
1336  auto state = iter.second->get_state();
1337 
1338  if (is_state_live(state) == false)
1339  {
1340  iter.second->lock();
1341  process_admin_commands(iter.second);
1342  iter.second->unlock();
1343  }
1344  }
1345 
1346  }
1347 
1348  void process()
1349  {
1350  static_assert(!Transport::is_multithreaded());
1351 
1352  if (llfix_likely(this->m_is_stopping.load() == false))
1353  {
1354  for (const auto& session_entry : m_sessions)
1355  {
1356  FixSession* current_session = session_entry.second;
1357 
1358  process_admin_commands(current_session);
1359 
1360  auto current_state = current_session->get_state();
1361 
1362  if (is_state_live(current_state))
1363  {
1364  process_outgoing_resend_request_if_necessary(current_session, VDSO::nanoseconds_monotonic());
1365  process_outgoing_test_request_if_necessary(current_session, VDSO::nanoseconds_monotonic());
1366  respond_to_resend_request_if_necessary(current_session);
1367  respond_to_test_request_if_necessary(current_session);
1368  send_heartbeat_if_necessary(current_session, VDSO::nanoseconds_monotonic());
1369 
1370  process_schedule_validator(current_session);
1371  }
1372  }
1373  }
1374  else
1375  {
1376  send_logout_messages_to_all_sessions();
1377  }
1378  }
1379 
1380  void push_admin_command_internal(const std::string& session_name, ModifyingAdminCommandType type, uint32_t arg = 0)
1381  {
1382  auto admin_command = new (std::nothrow) ModifyingAdminCommand;
1383 
1384  if (llfix_likely(admin_command))
1385  {
1386  admin_command->type = type;
1387  admin_command->arg = arg;
1388  m_sessions[session_name]->get_admin_commands()->push(admin_command); // Session name existence is checked in management server layer
1389  }
1390  else
1391  {
1392  LLFIX_LOG_ERROR("Failed to process setter admin command for FixServer " + m_name);
1393  }
1394  }
1395 
1396  void process_admin_commands(FixSession* session)
1397  {
1398  ModifyingAdminCommand* admin_command{ nullptr };
1399 
1400  if (session->get_admin_commands()->try_pop(&admin_command) == true)
1401  {
1402  switch (admin_command->type)
1403  {
1404  case ModifyingAdminCommandType::SET_INCOMING_SEQUENCE_NUMBER:
1405  {
1406  session->get_sequence_store()->set_incoming_seq_no(admin_command->arg);
1407  LLFIX_LOG_DEBUG(m_name + " : processed set incoming sequence number admin command , session name : " + session->get_name() + " , new seq no : " + std::to_string(admin_command->arg) );
1408  break;
1409  }
1410 
1411  case ModifyingAdminCommandType::SET_OUTGOING_SEQUENCE_NUMBER:
1412  {
1413  session->get_sequence_store()->set_outgoing_seq_no(admin_command->arg);
1414  LLFIX_LOG_DEBUG(m_name + " : processed set outgoing sequence number admin command , session name : " + session->get_name() + " , new seq no : " + std::to_string(admin_command->arg) );
1415  break;
1416  }
1417 
1418  case ModifyingAdminCommandType::SEND_SEQUENCE_RESET:
1419  {
1420  if( session->get_state() == SessionState::LOGGED_ON )
1421  {
1422  send_sequence_reset_message(session, admin_command->arg);
1423  LLFIX_LOG_DEBUG(m_name + " : processed send sequence reset admin command , session name : " + session->get_name() + " , new seq no : " + std::to_string(admin_command->arg) );
1424  }
1425  else
1426  {
1427  LLFIX_LOG_ERROR(m_name + " : dropping send sequence reset admin command as not logged on , session name : " + session->get_name());
1428  }
1429 
1430  break;
1431  }
1432 
1433  case ModifyingAdminCommandType::DISABLE_SESSION:
1434  {
1435  disable_session(session);
1436  LLFIX_LOG_DEBUG(m_name + " : processed disable session admin command for session " + session->get_name() + " , new session state : Disabled");
1437  break;
1438  }
1439 
1440  case ModifyingAdminCommandType::ENABLE_SESSION:
1441  {
1442  if(m_is_ha_primary == true)
1443  {
1444  enable_session(session);
1445  LLFIX_LOG_DEBUG(m_name + " : processed enable session admin command for " + session->get_name());
1446  }
1447  else
1448  {
1449  LLFIX_LOG_ERROR(m_name + " ignoring enable session admin command for " + session->get_name() + " since not a primary instance" );
1450  }
1451  break;
1452  }
1453 
1454  case ModifyingAdminCommandType::SET_IS_HA_PRIMARY_INSTANCE:
1455  {
1456  m_is_ha_primary = admin_command->arg == 1 ? true : false;
1457 
1458  if(admin_command->arg == 1)
1459  {
1460  enable_session(session);
1461  LLFIX_LOG_DEBUG(m_name + " : processed set is ha primary instance 1 admin command for " + session->get_name());
1462  }
1463  else
1464  {
1465  disable_session(session);
1466  LLFIX_LOG_DEBUG(m_name + " : processed set is ha primary instance 0 admin command for " + session->get_name());
1467  }
1468 
1469  break;
1470  }
1471 
1472 
1473  default: break;
1474  }
1475 
1476  delete admin_command;
1477  }
1478  }
1479 
1480  void disable_session(FixSession* session)
1481  {
1482  if (is_state_live(session->get_state()))
1483  {
1484  this->process_connection_closure(m_fix_connectors.get_peer_index(session), true);
1485  }
1486  session->set_state(SessionState::DISABLED);
1487  }
1488 
1489  void enable_session(FixSession* session)
1490  {
1491  if(session->get_state() == SessionState::DISABLED)
1492  {
1493  session->set_state(SessionState::DISCONNECTED);
1494  if(m_settings.refresh_resend_cache_during_promotion)
1495  session->reinitialise_outgoing_serialiser();
1496  }
1497  }
1498 
1500  // ADMIN UTILITY
1501  void process_outgoing_resend_request_if_necessary(FixSession* session, uint64_t current_timestamp_nanoseconds)
1502  {
1503  if (session->get_state() == SessionState::IN_RETRANSMISSION_INITIATED_BY_SELF)
1504  {
1505  // TERMINATE SESSION IF NECESSARY
1506  uint64_t delta_nanoseconds = current_timestamp_nanoseconds - session->outgoing_resend_request_timestamp_nanoseconds();
1507  uint64_t required_delta_nanoseconds = static_cast<uint64_t>(session->outgoing_resend_request_expire_secs()) * 1'000'000'000;
1508 
1509  if (delta_nanoseconds >= required_delta_nanoseconds)
1510  {
1511  // We need to terminate the session as the other side didn't respond properly to our outgoing resend request
1512  LLFIX_LOG_DEBUG("FixServer " + m_name + " : terminating session " + session->get_name() + " as the other end didn't respond to 35=2/resend request in pre-configured timeout (outgoing_resend_request_expire_secs) : " + std::to_string(session->outgoing_resend_request_expire_secs()));
1513  this->process_connection_closure(m_fix_connectors.get_peer_index(session));
1514  }
1515  }
1516  else
1517  {
1518  // SEND IF NECESSARY
1519  if (session->needs_to_send_resend_request())
1520  {
1521  LLFIX_LOG_DEBUG("FixServer " + m_name + " session " + session->get_name() + " peer " + std::to_string(m_fix_connectors.get_peer_index(session)) + " : sending resend request , begin no : " + std::to_string(session->get_outgoing_resend_request_begin_no()));
1522  send_resend_request(session);
1523  session->set_outgoing_resend_request_timestamp_nanoseconds(current_timestamp_nanoseconds);
1524  session->set_state(llfix::SessionState::IN_RETRANSMISSION_INITIATED_BY_SELF);
1525  session->set_needs_to_send_resend_request(false);
1526  }
1527  }
1528  }
1529 
1530  void process_outgoing_test_request_if_necessary(FixSession* session, uint64_t current_timestamp_nanoseconds)
1531  {
1532  if (session->expecting_response_for_outgoing_test_request() == false)
1533  {
1534  // SEND IF NECESSARY
1535  auto delta_nanoseconds = current_timestamp_nanoseconds - session->last_received_message_timestamp_nanoseconds();
1536 
1537  if (delta_nanoseconds >= (session->get_outgoing_test_request_interval_in_nanoseconds()))
1538  {
1539  LLFIX_LOG_DEBUG("FixServer " + m_name + " session " + session->get_name() + " : sending test request");
1540  send_test_request(session);
1541  session->set_outgoing_test_request_timestamp_nanoseconds(current_timestamp_nanoseconds);
1542  session->set_expecting_response_for_outgoing_test_request(true);
1543  }
1544  }
1545  else
1546  {
1547  // TERMINATE SESSION IF NECESSARY
1548  auto delta_nanoseconds = current_timestamp_nanoseconds - session->outgoing_test_request_timestamp_nanoseconds();
1549 
1550  if (delta_nanoseconds >= (session->get_heartbeart_interval_in_nanoseconds() * static_cast<uint64_t>(2)))
1551  {
1552  session->set_expecting_response_for_outgoing_test_request(false);
1553  LLFIX_LOG_DEBUG("FixServer " + m_name + " : terminating session " + session->get_name() + " as the other end didn't respond to 35=1/test request");
1554  this->process_connection_closure(m_fix_connectors.get_peer_index(session));
1555  }
1556  }
1557  }
1558 
1559  void respond_to_resend_request_if_necessary(FixSession* session)
1560  {
1561  if (session->needs_responding_to_incoming_resend_request())
1562  {
1563  const auto last_outgoing_seq_no = session->get_sequence_store()->get_outgoing_seq_no();
1564 
1565  // Partial replays not supported therefore incoming end no should be either 0 or same as last outgoing seq no
1566  bool will_replay = (session->serialisation_enabled()) && (session->replay_messages_on_incoming_resend_request() && (session->get_incoming_resend_request_end_no() == 0
1567  || session->get_incoming_resend_request_end_no() == last_outgoing_seq_no) && session->get_incoming_resend_request_begin_no()<=last_outgoing_seq_no);
1568 
1569  if(last_outgoing_seq_no > session->get_incoming_resend_request_begin_no())
1570  if(last_outgoing_seq_no-session->get_incoming_resend_request_begin_no()+1 > session->max_resend_range())
1571  will_replay = false;
1572 
1573  if(will_replay)
1574  {
1575  for (uint32_t i = session->get_incoming_resend_request_begin_no(); i <= last_outgoing_seq_no; i++)
1576  {
1577  if (session->get_outgoing_message_serialiser()->has_message_in_memory(i) == false)
1578  {
1579  will_replay = false;
1580  break;
1581  }
1582  }
1583  }
1584 
1585  if(will_replay)
1586  {
1587  LLFIX_LOG_DEBUG("FixServer " + m_name + " session " + session->get_name() + " : replaying messages , begin seq no : " + std::to_string(session->get_incoming_resend_request_begin_no()) + " , end seq no : " + std::to_string(last_outgoing_seq_no));
1588  resend_messages_to_client(session, session->get_incoming_resend_request_begin_no(), last_outgoing_seq_no);
1589  }
1590  else
1591  {
1592  LLFIX_LOG_DEBUG("FixServer " + m_name + " session " + session->get_name() + " : sending gap fill message");
1593  send_gap_fill_message(session);
1594  }
1595 
1596  session->set_needs_responding_to_incoming_resend_request(false);
1597  session->set_state(llfix::SessionState::LOGGED_ON);
1598  }
1599  }
1600 
1601  void respond_to_test_request_if_necessary(FixSession* session)
1602  {
1603  if (session->needs_responding_to_incoming_test_request())
1604  {
1605  if (session->get_incoming_test_request_id()->length() > 0)
1606  {
1607  send_heartbeat(session , session->get_incoming_test_request_id());
1608  session->get_incoming_test_request_id()->set_length(0);
1609  }
1610  else
1611  {
1612  // We should not hit here , but better than risking disconnection
1613  send_heartbeat(session, nullptr);
1614  }
1615 
1616  LLFIX_LOG_DEBUG("FixServer " + m_name + " session " + session->get_name() + " : responded to incoming test request");
1617  session->set_needs_responding_to_incoming_test_request(false);
1618  }
1619  }
1620 
1621  void send_heartbeat_if_necessary(FixSession* session, uint64_t current_timestamp_nanoseconds)
1622  {
1623  auto delta_nanoseconds = current_timestamp_nanoseconds - session->last_sent_message_timestamp_nanoseconds();
1624 
1625  if (delta_nanoseconds >= (session->get_heartbeart_interval_in_nanoseconds() * static_cast<uint64_t>(9) / static_cast<uint64_t>(10))) // 0.9 for safety
1626  {
1627  send_heartbeat(session, nullptr);
1628  }
1629  }
1630 
1631  void send_logout_messages_to_all_sessions()
1632  {
1633  for (const auto& session_entry : m_sessions)
1634  {
1635  FixSession* current_session = session_entry.second;
1636 
1637  if (current_session->get_state() != SessionState::DISCONNECTED)
1638  {
1639  send_logout_message(current_session);
1640  on_client_disconnected(current_session);
1641  }
1642  }
1643  }
1645  // TO CLIENT
1646  template <bool increment_outgoing_seq_no>
1647  LLFIX_FORCE_INLINE bool send_bytes(FixSession* session, const char* buffer, std::size_t buffer_size)
1648  {
1649  std::size_t peer_index = m_fix_connectors.get_peer_index(session);
1650 
1651  if (peer_index == static_cast<std::size_t>(-1))
1652  return false;
1653 
1654  auto sequence_store = session->get_sequence_store();
1655 
1656  auto ret = this->send(peer_index, buffer, buffer_size);
1657 
1658  if constexpr (increment_outgoing_seq_no == true)
1659  {
1660  if(ret==true)
1661  sequence_store->increment_outgoing_seq_no();
1662  }
1663 
1664  if(session->serialisation_enabled())
1665  session->get_outgoing_message_serialiser()->write(reinterpret_cast<const void*>(buffer), buffer_size, ret, sequence_store->get_outgoing_seq_no());
1666 
1667  if (m_message_persist_plugin)
1668  m_message_persist_plugin->persist_outgoing_message(session->get_name(), sequence_store->get_outgoing_seq_no(), buffer, buffer_size, ret);
1669 
1670  session->set_last_sent_message_timestamp_nanoseconds(VDSO::nanoseconds_monotonic());
1671 
1672  return ret;
1673  }
1674 
1676  // THREAD LOCAL UNKNOWN SESSION CONTEXT
1677  UnknownSessionContext* get_thread_local_unknown_session_context()
1678  {
1679  const std::lock_guard<UserspaceSpinlock<>> lock(m_unknown_session_context_lock);
1680 
1681  UnknownSessionContext* unknown_session_context = nullptr;
1682  unknown_session_context = reinterpret_cast<UnknownSessionContext*>(ThreadLocalStorage::get_instance().get());
1683 
1684  if (unknown_session_context == nullptr)
1685  {
1686  unknown_session_context = new(std::nothrow) UnknownSessionContext;
1687  ThreadLocalStorage::get_instance().set(unknown_session_context);
1688  }
1689 
1690  if (unknown_session_context)
1691  {
1692  if (unknown_session_context->m_incoming_fix_message.initialise() == false)
1693  {
1694  LLFIX_LOG_ERROR(m_name + " : Failed to initialise unknown session context's incoming_fix_message");
1695  return nullptr;
1696  }
1697 
1698  if (unknown_session_context->m_fix_string_view_cache.create(256) == false)
1699  {
1700  LLFIX_LOG_ERROR(m_name + " : Failed to create unknown session context's fix string view cache");
1701  return nullptr;
1702  }
1703  }
1704 
1705  return unknown_session_context;
1706  }
1707 
1708  void destroy_thread_local_unknown_session_context()
1709  {
1710  const std::lock_guard<UserspaceSpinlock<>> lock(m_unknown_session_context_lock);
1711 
1712  UnknownSessionContext* unknown_session_context{ nullptr };
1713  unknown_session_context = reinterpret_cast<UnknownSessionContext*>(ThreadLocalStorage::get_instance().get());
1714 
1715  if (unknown_session_context)
1716  {
1717  delete unknown_session_context;
1718  ThreadLocalStorage::get_instance().set(nullptr);
1719  }
1720  }
1721 
1723  // FROM CLIENT
1724  LLFIX_HOT void process_rx_buffer(std::size_t peer_index, char* buffer, std::size_t buffer_size)
1725  {
1726  std::size_t buffer_read_index = 0;
1727 
1728  if (llfix_unlikely(buffer_size < MINIMUM_REQUIRED_INITIAL_BUFFER_FOR_PARSING))
1729  {
1730  this->set_incomplete_buffer(peer_index, buffer, buffer_size);
1731  return;
1732  }
1733 
1734  FixSession* session = m_fix_connectors.get_session(peer_index);
1735 
1737  // FIND OUT THE END
1738  int final_tag10_delimiter_index{ -1 };
1739  int current_index = static_cast<int>(buffer_size - 1);
1740 
1741  if(llfix_unlikely(FixUtilities::find_delimiter_from_end(buffer, buffer_size, current_index) == false))
1742  {
1743  // We don't have the entire message
1744  this->set_incomplete_buffer(peer_index, buffer, buffer_size);
1745  return;
1746  }
1747 
1748  FixUtilities::find_tag10_start_from_end(buffer, buffer_size, current_index, final_tag10_delimiter_index);
1749 
1750  if (final_tag10_delimiter_index == -1)
1751  {
1752  // We don't have the entire message
1753  this->set_incomplete_buffer(peer_index, buffer, buffer_size);
1754  return;
1755  }
1757  // AT THIS POINT WE HAVE AT LEAST ONE COMPLETE MESSAGE
1758  while (true)
1759  {
1761  // FIND OUT THE BEGIN STRING
1762  int begin_string_offset{-1};
1763  FixUtilities::find_begin_string_position(buffer+buffer_read_index, buffer_size-buffer_read_index, begin_string_offset);
1764 
1765  if(llfix_likely(begin_string_offset>=0))
1766  {
1767  buffer_read_index += begin_string_offset;
1768  }
1769  else
1770  {
1771  LLFIX_LOG_ERROR(m_name + " received a message with no begin string : " + FixUtilities::fix_to_human_readible(buffer+buffer_read_index, buffer_size-buffer_read_index));
1772  return;
1773  }
1775  IncomingFixMessage* incoming_fix_message{ nullptr };
1776  ObjectCache<FixStringView>* fix_string_view_cache{ nullptr };
1777 
1778  if (llfix_likely(session != nullptr))
1779  {
1780  incoming_fix_message = session->get_incoming_fix_message();
1781  fix_string_view_cache = session->get_fix_string_view_cache();
1782  }
1783  else
1784  {
1785  UnknownSessionContext* unknown_session_context = get_thread_local_unknown_session_context();
1786 
1787  if (unknown_session_context)
1788  {
1789  incoming_fix_message = &(unknown_session_context->m_incoming_fix_message);
1790  fix_string_view_cache = &(unknown_session_context->m_fix_string_view_cache);
1791  }
1792  else
1793  {
1794  this->set_incomplete_buffer(peer_index, buffer, buffer_size);
1795  return;
1796  }
1797  }
1798 
1799  incoming_fix_message->reset();
1800  fix_string_view_cache->reset_pointer();
1801 
1802  current_index = static_cast<int>(buffer_read_index);
1803  bool looking_for_equals = true;
1804 
1805  int current_tag_start = static_cast<int>(buffer_read_index);
1806  int current_tag_len{ 0 };
1807 
1808  int current_value_start = static_cast<int>(buffer_read_index);
1809  int current_value_len{ 0 };
1810 
1811  int current_tag_10_delimiter_index = -1;
1812  int current_tag_35_tag_start_index = -1;
1813 
1814  uint32_t parser_reject_code = static_cast<uint32_t>(-1);
1815  uint32_t current_tag_index = 0;
1816 
1817  uint32_t encoded_current_message_type = static_cast<uint32_t>(-1);
1818 
1819  bool in_a_repeating_group{ false };
1820  uint32_t current_rg_count_tag{ 0 };
1821 
1822  #ifdef LLFIX_ENABLE_BINARY_FIELDS
1823  int binary_field_length = 0;
1824  int binary_field_counter = 0;
1825  #endif
1826 
1827  while (true)
1828  {
1829  char current_char = buffer[current_index];
1830 
1831  if (looking_for_equals)
1832  {
1833  if (current_char == FixConstants::FIX_EQUALS)
1834  {
1835  current_tag_len = current_index - current_tag_start;
1836  current_value_start = current_index + 1;
1837  looking_for_equals = false;
1838  }
1839  else if (llfix_unlikely(current_char == FixConstants::FIX_DELIMITER))
1840  {
1841  current_tag_start = current_index + 1;
1842  parser_reject_code = FixParserErrorCodes::NO_EQUALS_SIGN;
1843  }
1844  }
1845  #ifdef LLFIX_ENABLE_BINARY_FIELDS
1846  else
1847  {
1848  if(llfix_unlikely(binary_field_length>0))
1849  if(binary_field_counter < binary_field_length)
1850  binary_field_counter++;
1851  }
1852 
1853  if (looking_for_equals == false && current_char == FixConstants::FIX_DELIMITER)
1854  {
1855  bool reached_value_end = true;
1856 
1857  if(llfix_unlikely(binary_field_length>0))
1858  {
1859  if(binary_field_length>binary_field_counter)
1860  {
1861  reached_value_end = false;
1862  }
1863  else if(binary_field_length == binary_field_counter)
1864  {
1865  binary_field_length=0;
1866  binary_field_counter=0;
1867  }
1868  }
1869 
1870  if(llfix_likely(reached_value_end))
1871  {
1872 
1873  #else
1874  else if (current_char == FixConstants::FIX_DELIMITER)
1875  {
1876  #endif
1877  current_value_len = current_index - current_value_start;
1878 
1879  if (llfix_unlikely(current_value_len == 0))
1880  {
1881  parser_reject_code = FixConstants::FIX_ERROR_CODE_TAG_WITHOUT_VALUE;
1882  }
1883 
1884  bool is_current_tag_numeric{ true };
1885  FixSession::validate_tag_format(buffer + current_tag_start, static_cast<std::size_t>(current_tag_len), is_current_tag_numeric, parser_reject_code);
1886 
1887  if (llfix_likely(is_current_tag_numeric))
1888  {
1890  // RECORD THE CURRENT TAG VALUE PAIR
1891  uint32_t tag = Converters::chars_to_unsigned_int<uint32_t>(buffer + current_tag_start, static_cast<std::size_t>(current_tag_len));
1892 
1893  if(llfix_unlikely(tag == 0))
1894  {
1895  parser_reject_code = FixConstants::FIX_ERROR_CODE_INVALID_TAG_NUMBER;
1896  }
1897 
1898  FixStringView* value = fix_string_view_cache->allocate();
1899  value->set_buffer(const_cast<char*>(buffer + current_value_start), static_cast<std::size_t>(current_value_len));
1900 
1902  current_tag_index++;
1903  FixSession::validate_header_tags_order(tag, current_tag_index, parser_reject_code);
1905 
1906  // Some tags may be used as a group member but also as an individual tag so we can't fully rely on is_a_repeating_group_tag
1907  // and need to detect start and end of a group
1908  if(encoded_current_message_type != static_cast<uint32_t>(-1))
1909  {
1910  if(llfix_likely(!in_a_repeating_group))
1911  {
1912  if (llfix_unlikely(FixSession::get_repeating_group_specs().is_a_repeating_group_count_tag(encoded_current_message_type, tag)))
1913  {
1914  current_rg_count_tag = tag;
1915  in_a_repeating_group = true;
1916  }
1917  }
1918  else
1919  {
1920  if (llfix_likely(FixSession::get_repeating_group_specs().is_a_repeating_group_tag(encoded_current_message_type, current_rg_count_tag, tag) == false))
1921  {
1922  in_a_repeating_group = false;
1923  }
1924  }
1925 
1926  #ifdef LLFIX_ENABLE_BINARY_FIELDS
1927  if (llfix_unlikely(FixSession::get_binary_field_specs().is_binary_data_length_tag(encoded_current_message_type, tag)))
1928  {
1929  binary_field_length = Converters::chars_to_int<int>(value->data(), value->length());
1930  }
1931  #endif
1932  }
1933 
1934  if (llfix_likely(in_a_repeating_group == false))
1935  {
1936  if (llfix_likely(incoming_fix_message->has_tag(tag) == false))
1937  {
1938  incoming_fix_message->set_tag(tag, value);
1939  }
1940  else
1941  {
1942  parser_reject_code = FixConstants::FIX_ERROR_CODE_TAG_APPEARS_MORE_THAN_ONCE;
1943  }
1944  }
1945  else
1946  {
1947  incoming_fix_message->set_repeating_group_tag(tag, value);
1948  }
1949 
1950  if (tag == FixConstants::TAG_CHECKSUM)
1951  {
1952  current_tag_10_delimiter_index = current_index;
1954  FixSession::validate_tag9_and_tag35((incoming_fix_message), current_tag_start, current_tag_35_tag_start_index, parser_reject_code);
1956  break;
1957  }
1958  else if (tag == FixConstants::TAG_MSG_TYPE)
1959  {
1960  current_tag_35_tag_start_index = current_tag_start;
1961  encoded_current_message_type = FixUtilities::pack_message_type(value->to_string_view());
1962  }
1963  } // if (llfix_likely(is_current_tag_numeric))
1964 
1965  current_tag_start = current_index + 1;
1966  looking_for_equals = true;
1967  #ifdef LLFIX_ENABLE_BINARY_FIELDS
1968  } // if(llfix_likely(reached_value_end))
1969  #endif
1970  } // else if (current_char == FixConstants::FIX_DELIMITER) or if (looking_for_equals == false && current_char == FixConstants::FIX_DELIMITER)
1971 
1972  // Apart from the check below (else if (static_cast<int>(buffer_read_index) > final_tag10_delimiter_index)),
1973  // we also need to check here if found last tag 10 delimiter is before the found tag8
1974  if (current_index >= static_cast<int>(buffer_size) - 1)
1975  {
1976  this->set_incomplete_buffer(peer_index, buffer + buffer_read_index, buffer_size - buffer_read_index);
1977  return;
1978  }
1979 
1980  current_index++;
1981  } // while
1982 
1983  auto current_message_length = current_tag_10_delimiter_index + 1 - buffer_read_index;
1985  if (llfix_unlikely(session == nullptr))
1986  {
1987  session = accept_session(peer_index, incoming_fix_message, buffer + buffer_read_index, current_message_length, parser_reject_code);
1988 
1989  if (session == nullptr) // Means that it was an invalid logon attempt and we already closed the connection
1990  {
1991  return;
1992  }
1993  }
1995  session->lock();
1996  process_incoming_fix_message(session, peer_index, buffer + buffer_read_index, current_message_length, parser_reject_code);
1997  session->unlock();
1998 
1999  #ifndef LLFIX_UNIT_TEST
2000  if(llfix_unlikely(session->get_state() == SessionState::DISCONNECTED)) // process_incoming_fix_message may terminate connection
2001  {
2002  return;
2003  }
2004  #endif
2005 
2006  buffer_read_index = current_tag_10_delimiter_index + 1;
2007 
2008  if (current_tag_10_delimiter_index == static_cast<int>(buffer_size) - 1)
2009  {
2010  this->reset_incomplete_buffer(peer_index);
2011  return;
2012  }
2013  else if (static_cast<int>(buffer_read_index) > final_tag10_delimiter_index)
2014  {
2015  this->set_incomplete_buffer(peer_index, buffer + buffer_read_index, buffer_size - buffer_read_index);
2016  return;
2017  }
2018  }
2019  }
2020 
2021  void process_invalid_logon(std::size_t peer_index, const std::string& log_message, bool send_logout = false, const std::string& logout_reason_text = "")
2022  {
2023  if(!log_message.empty())
2024  {
2025  int peer_port{0};
2026  std::string peer_ip;
2027  this->get_peer_details(peer_index, peer_ip, peer_port);
2028 
2029  LLFIX_LOG_ERROR("Invalid logon attempt from " + peer_ip + " : " + log_message);
2030  }
2031 
2032  process_connection_closure(peer_index, send_logout, logout_reason_text);
2033  }
2034 
2035  void process_connection_closure(std::size_t peer_index, bool send_logout = false, const std::string& logout_reason_text = "")
2036  {
2037  if (send_logout)
2038  {
2039  auto session = m_fix_connectors.get_session(peer_index);
2040 
2041  if (session != nullptr)
2042  {
2043  send_logout_message(session, logout_reason_text);
2044  }
2045  }
2046 
2047  this->close_connection(peer_index);
2048  }
2049 
2050  void process_incoming_fix_message(FixSession* session, std::size_t peer_index, const char* buffer_message, std::size_t buffer_message_length, uint32_t parser_reject_code)
2051  {
2052  session->set_last_received_message_timestamp_nanoseconds(VDSO::nanoseconds_monotonic());
2053 
2054  if(session->serialisation_enabled())
2055  session->get_incoming_message_serialiser()->write(buffer_message, buffer_message_length, true);
2056 
2057  if (llfix_unlikely(session->expecting_response_for_outgoing_test_request()))
2058  {
2059  // We are permissive , any message not just 35=0 with expected t112, satisfies our outgoing test request
2060  session->set_expecting_response_for_outgoing_test_request(false);
2061  LLFIX_LOG_DEBUG("FixServer " + m_name + " session " + session->get_name() + " : other end satisfied the test request");
2062  }
2064  if (session->validations_enabled())
2065  {
2066  uint32_t reject_message_code = static_cast<uint32_t>(-1);
2067 
2068  if (session->validate_fix_message(*session->get_incoming_fix_message(), buffer_message, buffer_message_length, parser_reject_code, reject_message_code) == false)
2069  {
2070  if (reject_message_code != static_cast<uint32_t>(-1))
2071  {
2072  send_reject_message(session, session->get_incoming_fix_message(), reject_message_code, buffer_message, buffer_message_length, session->get_last_error_tag());
2073  }
2074 
2075  return;
2076  }
2077  }
2079  // SEQUENCE NO & MESSAGE PERSIST PLUGIN
2080  auto sequence_store = session->get_sequence_store();
2081  sequence_store->increment_incoming_seq_no();
2082  auto sequence_store_incoming_seq_no = sequence_store->get_incoming_seq_no();
2083 
2084  if (m_message_persist_plugin)
2085  m_message_persist_plugin->persist_incoming_message(session->get_name(), sequence_store_incoming_seq_no, buffer_message, buffer_message_length);
2086 
2088  // SEQUENCE NO CHECKS
2089  auto incoming_seq_no = session->get_incoming_fix_message()->get_tag_value_as<uint32_t>(FixConstants::TAG_MSG_SEQ_NUM);
2090 
2091  if (llfix_unlikely(incoming_seq_no > sequence_store_incoming_seq_no))
2092  {
2093  if(FixSession::is_a_hard_sequence_reset_message(*session->get_incoming_fix_message()) == false)
2094  {
2095  if (session->get_state() != SessionState::PENDING_LOGON) // on_logon_request handles this case on its own
2096  {
2097  session->queue_outgoing_resend_request(sequence_store_incoming_seq_no, incoming_seq_no);
2098  return;
2099  }
2100  }
2101  }
2102  else if (llfix_unlikely(incoming_seq_no < sequence_store_incoming_seq_no))
2103  {
2104  if(FixSession::is_a_hard_sequence_reset_message(*session->get_incoming_fix_message()) == false)
2105  {
2106  std::string logout_reason_text;
2107  bool send_logout = false;
2108 
2109  if (session->get_state() == SessionState::PENDING_LOGON)
2110  {
2111  send_logout = true;
2112  logout_reason_text = "MsgSeqNum too low, expecting " + std::to_string(sequence_store_incoming_seq_no) + " but received " + std::to_string(incoming_seq_no);
2113  }
2114 
2115  sequence_store->set_incoming_seq_no(sequence_store_incoming_seq_no - 1);
2116  LLFIX_LOG_DEBUG("FixServer " + m_name + " : terminating session " + session->get_name() + " as the incoming sequence no (" + std::to_string(incoming_seq_no) + ") is lower than expected (" + std::to_string(sequence_store_incoming_seq_no) + ")");
2117  this->process_connection_closure(peer_index, send_logout, logout_reason_text);
2118  return;
2119  }
2120  }
2121 
2122  if (llfix_unlikely(session->get_state() == SessionState::IN_RETRANSMISSION_INITIATED_BY_SELF))
2123  {
2124  if (incoming_seq_no == session->get_outgoing_resend_request_end_no())
2125  {
2126  LLFIX_LOG_DEBUG("FixServer " + m_name + " : other end satisfied the resend request");
2127  session->set_state(llfix::SessionState::LOGGED_ON);
2128  }
2129  }
2131  if(process_incoming_throttling(session, session->get_incoming_fix_message()) == false)
2132  {
2133  return;
2134  }
2135 
2136  auto message_type = session->get_incoming_fix_message()->get_tag_value(FixConstants::TAG_MSG_TYPE);
2137 
2138  if (llfix_likely(message_type->length() == 1))
2139  {
2140  switch (message_type->data()[0])
2141  {
2142  case FixConstants::MSG_TYPE_NEW_ORDER: on_new_order(session, session->get_incoming_fix_message()); break;
2143  case FixConstants::MSG_TYPE_ORDER_CANCEL: on_cancel_order(session, session->get_incoming_fix_message()); break;
2144  case FixConstants::MSG_TYPE_ORDER_CANCEL_REPLACE: on_replace_order(session, session->get_incoming_fix_message()); break;
2145  case FixConstants::MSG_TYPE_HEARTBEAT: on_client_heartbeat(session); break;
2146  case FixConstants::MSG_TYPE_TEST_REQUEST: session->process_test_request_message(*session->get_incoming_fix_message()); on_client_test_request(session, session->get_incoming_fix_message()); break;
2147  case FixConstants::MSG_TYPE_RESEND_REQUEST: session->process_resend_request(*session->get_incoming_fix_message()); on_client_resend_request(session, session->get_incoming_fix_message()); break;
2148  case FixConstants::MSG_TYPE_REJECT: on_session_level_reject(session, session->get_incoming_fix_message()); break;
2149  case FixConstants::MSG_TYPE_BUSINESS_REJECT: on_application_level_reject(session, session->get_incoming_fix_message()); break;
2150  case FixConstants::MSG_TYPE_LOGON: process_logon_request(session, peer_index, session->get_incoming_fix_message()); break;
2151  case FixConstants::MSG_TYPE_LOGOUT: process_logout_request(session, peer_index, session->get_incoming_fix_message()); break;
2152  case FixConstants::MSG_TYPE_SEQUENCE_RESET: if (session->process_incoming_sequence_reset_message(*session->get_incoming_fix_message()) == false) { send_reject_message(session, session->get_incoming_fix_message(), FixConstants::FIX_ERROR_CODE_VALUE_INCORRECT_FOR_TAG, buffer_message, buffer_message_length, FixConstants::TAG_NEW_SEQ_NO); }; break;
2153  // Anything else
2154  default: on_custom_message(session, session->get_incoming_fix_message()); break;
2155  }
2156  }
2157  else
2158  {
2159  on_custom_message(session, session->get_incoming_fix_message());
2160  }
2161  }
2162 
2163  bool validate_logon_request(FixSession* session, std::size_t peer_index, const IncomingFixMessage* message)
2164  {
2165  LLFIX_UNUSED(session);
2166  int peer_port{0};
2167  std::string peer_ip;
2168  this->get_peer_details(peer_index, peer_ip, peer_port);
2169 
2170  if (message->has_tag(FixConstants::TAG_ENCRYPT_METHOD) == false)
2171  {
2172  LLFIX_LOG_ERROR("Incoming logon message for server " + m_name + " from " + peer_ip + " does not have required t98(encryption method), compid : " + message->get_tag_value_as<std::string>(FixConstants::TAG_SENDER_COMP_ID));
2173  return false;
2174  }
2175 
2176  if (message->has_tag(FixConstants::TAG_HEART_BT_INT) == false)
2177  {
2178  LLFIX_LOG_ERROR("Incoming logon message for server " + m_name + " from " + peer_ip + " does not have required t108(heartbeat interval), compid : " + message->get_tag_value_as<std::string>(FixConstants::TAG_SENDER_COMP_ID));
2179  return false;
2180  }
2181 
2182  auto t108_string = message->get_tag_value_as<std::string>(FixConstants::TAG_HEART_BT_INT);
2183 
2184  if (t108_string.find('-') != std::string::npos)
2185  {
2186  LLFIX_LOG_ERROR("Incoming logon message for server " + m_name + " from " + peer_ip + " has invalid(negative) t108(heartbeat interval) value, compid : " + message->get_tag_value_as<std::string>(FixConstants::TAG_SENDER_COMP_ID));
2187  return false;
2188  }
2189 
2190  if (t108_string.find('.') != std::string::npos)
2191  {
2192  LLFIX_LOG_ERROR("Incoming logon message for server " + m_name + " from " + peer_ip + " has invalid(decimal points) t108(heartbeat interval) value, compid : " + message->get_tag_value_as<std::string>(FixConstants::TAG_SENDER_COMP_ID));
2193  return false;
2194  }
2195 
2196  if (message->get_tag_value_as<uint32_t>(FixConstants::TAG_HEART_BT_INT) == 0)
2197  {
2198  LLFIX_LOG_ERROR("Incoming logon message for server " + m_name + " from " + peer_ip + " has invalid(zero) t108(heartbeat interval) value, compid : " + message->get_tag_value_as<std::string>(FixConstants::TAG_SENDER_COMP_ID));
2199  return false;
2200  }
2201 
2202  return true;
2203  }
2204 
2205  void do_post_logon_sequence_number_check(FixSession* session)
2206  {
2207  if(session->get_state() == SessionState::LOGGED_ON)
2208  {
2209  // IF LOGON WAS WITH A SEQ NO HIGHER THAN EXPECTED , WE NEED TO SEND A RESEND REQUEST
2210  auto sequence_store_incoming_seq_no = session->get_sequence_store()->get_incoming_seq_no();
2211  auto incoming_seq_no = session->get_incoming_fix_message()->get_tag_value_as<uint32_t>(FixConstants::TAG_MSG_SEQ_NUM);
2212 
2213  if (llfix_unlikely(incoming_seq_no > sequence_store_incoming_seq_no))
2214  {
2215  if(FixSession::is_a_hard_sequence_reset_message(*session->get_incoming_fix_message()) == false)
2216  {
2217  session->queue_outgoing_resend_request(sequence_store_incoming_seq_no, incoming_seq_no);
2218  }
2219  }
2220 
2221  // LOW SEQ NOS ARE HANDLED IN process_incoming_fix_message
2222  }
2223  }
2224 
2225  void process_logout_request(FixSession* session, std::size_t peer_index, const IncomingFixMessage* message)
2226  {
2227  send_logout_message(session);
2228  session->set_state(SessionState::LOGGED_OUT);
2229  LLFIX_LOG_DEBUG(m_name + ", session logged out : " + session->get_name() + " , peer index : " + std::to_string(peer_index));
2230  on_logout_request(session, message);
2231  }
2232 
2233  FixServer(const FixServer& other) = delete;
2234  FixServer& operator= (const FixServer& other) = delete;
2235  FixServer(FixServer&& other) = delete;
2236  FixServer& operator=(FixServer&& other) = delete;
2237 };
2238 
2239 } // namespace
llfix::FixServer::on_client_resend_request
virtual void on_client_resend_request(FixSession *session, const IncomingFixMessage *message)
Called when a Resend Request (35=2) is received from a client.
Definition: fix_server.h:634
llfix::FixServer::shutdown
void shutdown()
Shuts down the FIX server.
Definition: fix_server.h:556
llfix::FixServer::send_gap_fill_message
virtual bool send_gap_fill_message(FixSession *session)
Sends a Gap Fill message in response to a Resend Request.
Definition: fix_server.h:1072
llfix::OutgoingFixMessage
FIX message builder and encoder for outbound messages.
Definition: outgoing_fix_message.h:99
llfix::FixServer::process_schedule_validator
virtual void process_schedule_validator(FixSession *session)
Validates the session against configured schedule rules.
Definition: fix_server.h:701
llfix::FixServer::authenticate_logon_request
virtual bool authenticate_logon_request(FixSession *session, const IncomingFixMessage *message)
Authenticates an incoming Logon (35=A) request.
Definition: fix_server.h:882
llfix::FixServer::create
bool create(const std::string &server_name, const std::string server_config_file_path)
Creates and initialises the FIX server instance.
Definition: fix_server.h:189
llfix::FixServer::on_logon_request
virtual void on_logon_request(FixSession *session, const IncomingFixMessage *message)
Called when a Logon (35=A) request is received from a client.
Definition: fix_server.h:620
llfix::FixServer::get_session_name
std::string get_session_name(FixSession *session)
Retrieves the name of a FIX session.
Definition: fix_server.h:415
llfix::FixServer
FIX server implementation.
Definition: fix_server.h:168
llfix::FixSession::get_sequence_store
SequenceStore * get_sequence_store()
Provides access to the session's sequence store.
Definition: fix_session.h:363
llfix::FixServer::send_logout_message
virtual bool send_logout_message(FixSession *session, const std::string &reason_text="")
Sends a Logout (35=5) message.
Definition: fix_server.h:1008
llfix::FixServer::get_session_names
void get_session_names(std::vector< std::string > &target) override
Retrieves the names of all configured FIX sessions.
Definition: fix_server.h:435
llfix::FixServer::accept_session
virtual FixSession * accept_session(std::size_t peer_index, const IncomingFixMessage *incoming_fix_message, const char *buffer, std::size_t buffer_length, uint32_t parser_reject_code)
Accepts and validates an incoming session before logon processing.
Definition: fix_server.h:725
llfix::FixServer::add_session
bool add_session(const std::string &session_name, FixSessionSettings &session_settings)
Adds a FIX session using preloaded session settings.
Definition: fix_server.h:293
llfix::FixServer::send_outgoing_message
virtual bool send_outgoing_message(FixSession *session, OutgoingFixMessage *message)
Encodes and sends an outgoing FIX message.
Definition: fix_server.h:498
llfix::IncomingFixMessage::has_tag
bool has_tag(uint32_t tag) const
Checks whether a FIX tag exists and is valid.
Definition: incoming_fix_message.h:85
llfix::FixSession
Represents a single FIX protocol session.
Definition: fix_session.h:95
llfix::FixServer::send_sequence_reset_message
virtual bool send_sequence_reset_message(FixSession *session, uint32_t desired_sequence_no)
Sends a Sequence Reset (35=4) message without gap fill.
Definition: fix_server.h:1048
llfix::FixServer::get_session
FixSession * get_session(const std::string &session_name) override
Retrieves a FIX session by name.
Definition: fix_server.h:398
llfix::FixServer::on_client_test_request
virtual void on_client_test_request(FixSession *session, const IncomingFixMessage *message)
Called when a Test Request (35=1) is received from a client.
Definition: fix_server.h:641
llfix::FixServer::on_cancel_order
virtual void on_cancel_order(FixSession *session, const IncomingFixMessage *message)
Called when an Order Cancel Request (35=F) message is received.
Definition: fix_server.h:584
llfix::FixSession::get_name
std::string get_name() const override
Returns the logical name of the FIX session.
Definition: fix_session.h:278
llfix::FixServer::on_new_order
virtual void on_new_order(FixSession *session, const IncomingFixMessage *message)
Called when a New Order (35=D) message is received.
Definition: fix_server.h:577
llfix::FixServer::on_application_level_reject
virtual void on_application_level_reject(FixSession *session, const IncomingFixMessage *message)
Called when an application-level reject message is generated.
Definition: fix_server.h:598
llfix::FixServer::add_sessions_from
bool add_sessions_from(const std::string &session_config_file_path)
Adds all FIX sessions found in a configuration file.
Definition: fix_server.h:329
llfix::FixServer::send_test_request
virtual bool send_test_request(FixSession *session)
Sends a Test Request (35=1) message.
Definition: fix_server.h:1021
llfix::FixServer::on_client_heartbeat
virtual void on_client_heartbeat(FixSession *session)
Called when a Heartbeat (35=0) message is received from a client.
Definition: fix_server.h:647
llfix::FixServer::on_replace_order
virtual void on_replace_order(FixSession *session, const IncomingFixMessage *message)
Called when an Order Cancel/Replace Request (35=G) message is received.
Definition: fix_server.h:591
llfix::FixServer::add_session
bool add_session(const std::string &session_config_file_path, const std::string &session_name)
Adds a FIX session by loading settings from a configuration file.
Definition: fix_server.h:306
llfix::IncomingFixMessage
Represents a parsed incoming FIX message.
Definition: incoming_fix_message.h:67
llfix::FixUtilities::fix_to_human_readible
static std::string fix_to_human_readible(const char *buffer, std::size_t buffer_length)
Converts a FIX message buffer to a human-readable string.
Definition: fix_utilities.h:108
llfix::FixServer::specify_repeating_group
void specify_repeating_group(Args... args)
Specify repeating group definitions for incoming FIX messages for all sessions.
Definition: fix_server.h:457
llfix::FixServer::process_incoming_throttling
virtual bool process_incoming_throttling(FixSession *session, const IncomingFixMessage *incoming_fix_message)
Applies incoming message throttling logic.
Definition: fix_server.h:661
llfix::FixServer::outgoing_message_instance
OutgoingFixMessage * outgoing_message_instance(FixSession *session)
Retrieves the reusable outgoing FIX message instance for a session.
Definition: fix_server.h:485
llfix::SequenceStore::get_outgoing_seq_no
uint32_t get_outgoing_seq_no() const
Get the outgoing FIX sequence number.
Definition: sequence_store.h:127
llfix::FixServer::process_logon_request
virtual void process_logon_request(FixSession *session, std::size_t peer_index, const IncomingFixMessage *message)
Processes an incoming Logon (35=A) request.
Definition: fix_server.h:840
llfix::FixServer::on_logout_request
virtual void on_logout_request(FixSession *session, const IncomingFixMessage *message)
Called when a Logout (35=5) request is received from a client.
Definition: fix_server.h:627
llfix::FixServer::on_session_level_reject
virtual void on_session_level_reject(FixSession *session, const IncomingFixMessage *message)
Called when a session-level reject message is generated.
Definition: fix_server.h:605
llfix::FixServer::send_resend_request
virtual bool send_resend_request(FixSession *session)
Sends a Resend Request (35=2) message.
Definition: fix_server.h:1034
llfix::FixServer::get_session_count
std::size_t get_session_count() const
Returns the number of configured FIX sessions.
Definition: fix_server.h:381
llfix::FixServer::on_custom_message
virtual void on_custom_message(FixSession *session, const IncomingFixMessage *message)
Called when any other FIX message type is received.
Definition: fix_server.h:612
llfix::IncomingFixMessage::get_tag_value_as
T get_tag_value_as(uint32_t tag, std::size_t decimal_points=0) const
Retrieves a FIX tag value converted to the requested type.
Definition: incoming_fix_message.h:261