From e6f4d6c66f9a7f3632afca5e95c9040f1e97e4d9 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 2 Apr 2018 01:24:32 +0100 Subject: [PATCH 01/49] protocol: Packet class definition --- include/ki/protocol/Packet.h | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 include/ki/protocol/Packet.h diff --git a/include/ki/protocol/Packet.h b/include/ki/protocol/Packet.h new file mode 100644 index 0000000..79e2bc2 --- /dev/null +++ b/include/ki/protocol/Packet.h @@ -0,0 +1,56 @@ +#pragma once +#include "../util/Serializable.h" +#include +#include +#include +#include + +namespace ki +{ +namespace protocol +{ + class Packet : public util::Serializable + { + public: + Packet(bool control = false, uint8_t opcode = 0); + virtual ~Packet() = default; + + bool is_control() const; + void set_control(bool control); + + uint8_t get_opcode() const; + void set_opcode(uint8_t opcode); + + template + void set_payload_data(const DataT &data) + { + static_assert(std::is_base_of::value, + "DataT must derive from Serializable."); + + std::ostringstream oss; + data.write_to(oss); + m_payload = std::vector(oss.str().c_str(), data.get_size()); + } + + template + DataT *get_payload_data() const + { + static_assert(std::is_base_of::value, + "DataT must derive from Serializable."); + + std::istringstream iss(m_payload.data()); + DataT *data = new DataT(); + data->read_from(iss); + return data; + } + + virtual void write_to(std::ostream &ostream) const final; + virtual void read_from(std::istream &istream) final; + virtual size_t get_size() const final; + private: + bool m_control; + uint8_t m_opcode; + std::vector m_payload; + }; +} +} From eb7e5ade631ac4484c0c2878e2f3bb7e84618f4f Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 2 Apr 2018 01:25:08 +0100 Subject: [PATCH 02/49] protocol: Definitions for control structures --- include/ki/protocol/control/ClientHello.h | 49 ++++++++++++++++++++++ include/ki/protocol/control/Opcode.h | 20 +++++++++ include/ki/protocol/control/Ping.h | 50 +++++++++++++++++++++++ include/ki/protocol/control/ServerHello.h | 49 ++++++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 include/ki/protocol/control/ClientHello.h create mode 100644 include/ki/protocol/control/Opcode.h create mode 100644 include/ki/protocol/control/Ping.h create mode 100644 include/ki/protocol/control/ServerHello.h diff --git a/include/ki/protocol/control/ClientHello.h b/include/ki/protocol/control/ClientHello.h new file mode 100644 index 0000000..ee00b66 --- /dev/null +++ b/include/ki/protocol/control/ClientHello.h @@ -0,0 +1,49 @@ +#pragma once +#include "../../util/Serializable.h" +#include "../Packet.h" +#include "Opcode.h" +#include +#include + +namespace ki +{ +namespace protocol +{ +namespace control +{ + class ClientHello : public util::Serializable + { + public: + ClientHello(uint16_t session_id = 0, + uint64_t timestamp = 0, uint32_t milliseconds = 0); + virtual ~ClientHello() = default; + + uint16_t get_session_id() const; + void set_session_id(uint16_t session_id); + + uint64_t get_timestamp() const; + void set_timestamp(uint64_t timestamp); + + uint32_t get_milliseconds() const; + void set_milliseconds(uint32_t milliseconds); + + void write_to(std::ostream &ostream) const final; + void read_from(std::istream &istream) final; + size_t get_size() const final; + + static Packet *create_packet(uint16_t session_id = 0, + uint64_t timestamp = 0, uint32_t milliseconds = 0) + { + ClientHello data(session_id, timestamp, milliseconds); + auto *packet = new Packet(true, (uint8_t)Opcode::CLIENT_HELLO); + packet->set_payload_data(data); + return packet; + } + private: + uint16_t m_session_id; + uint64_t m_timestamp; + uint32_t m_milliseconds; + }; +} +} +} diff --git a/include/ki/protocol/control/Opcode.h b/include/ki/protocol/control/Opcode.h new file mode 100644 index 0000000..8442a56 --- /dev/null +++ b/include/ki/protocol/control/Opcode.h @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace ki +{ +namespace protocol +{ +namespace control +{ + enum class Opcode : uint8_t + { + SERVER_HELLO = 0, + UDP_HELLO = 1, + PING = 3, + PING_RSP = 4, + CLIENT_HELLO = 5 + }; +} +} +} \ No newline at end of file diff --git a/include/ki/protocol/control/Ping.h b/include/ki/protocol/control/Ping.h new file mode 100644 index 0000000..dff0ed9 --- /dev/null +++ b/include/ki/protocol/control/Ping.h @@ -0,0 +1,50 @@ +#pragma once +#include "../../util/Serializable.h" +#include "../Packet.h" +#include "Opcode.h" +#include + +namespace ki +{ + namespace protocol + { + namespace control + { + class Ping : public util::Serializable + { + public: + Ping(uint16_t session_id = 0, + uint16_t milliseconds = 0, uint8_t minutes = 0); + virtual ~Ping() = default; + + uint16_t get_session_id() const; + void set_session_id(uint16_t session_id); + + uint16_t get_milliseconds() const; + void set_milliseconds(uint16_t milliseconds); + + uint8_t get_minutes() const; + void set_minutes(uint8_t minutes); + + void write_to(std::ostream &ostream) const final; + void read_from(std::istream &istream) final; + size_t get_size() const final; + + static Packet *create_packet(uint16_t session_id = 0, + uint16_t milliseconds = 0, uint8_t minutes = 0, + bool response = false) + { + Ping data(session_id, milliseconds, minutes); + auto *packet = new Packet(true, + (uint8_t)(response ? Opcode::PING_RSP : Opcode::PING)); + packet->set_payload_data(data); + return packet; + } + private: + uint16_t m_session_id; + uint16_t m_milliseconds; + uint8_t m_minutes; + }; + } + } +} diff --git a/include/ki/protocol/control/ServerHello.h b/include/ki/protocol/control/ServerHello.h new file mode 100644 index 0000000..2c0d50e --- /dev/null +++ b/include/ki/protocol/control/ServerHello.h @@ -0,0 +1,49 @@ +#pragma once +#include "../../util/Serializable.h" +#include "../Packet.h" +#include "Opcode.h" +#include +#include + +namespace ki +{ +namespace protocol +{ +namespace control +{ + class ServerHello : public util::Serializable + { + public: + ServerHello(uint16_t session_id = 0, + uint64_t timestamp = 0, uint32_t milliseconds = 0); + virtual ~ServerHello() = default; + + uint16_t get_session_id() const; + void set_session_id(uint16_t session_id); + + uint64_t get_timestamp() const; + void set_timestamp(uint64_t timestamp); + + uint32_t get_milliseconds() const; + void set_milliseconds(uint32_t milliseconds); + + void write_to(std::ostream &ostream) const final; + void read_from(std::istream &istream) final; + size_t get_size() const final; + + static Packet *create_packet(uint16_t session_id = 0, + uint64_t timestamp = 0, uint32_t milliseconds = 0) + { + ServerHello data(session_id, timestamp, milliseconds); + auto *packet = new Packet(true, (uint8_t)Opcode::SERVER_HELLO); + packet->set_payload_data(data); + return packet; + } + private: + uint16_t m_session_id; + uint64_t m_timestamp; + uint32_t m_milliseconds; + }; +} +} +} From 3cb082b1af440127e67c5989b94a0b7d2e1b2b2b Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Tue, 3 Apr 2018 16:11:06 +0100 Subject: [PATCH 03/49] protocol: Definitions for DML messages --- include/ki/protocol/dml/Message.h | 38 +++++++++++++++++ include/ki/protocol/dml/MessageBuilder.h | 38 +++++++++++++++++ include/ki/protocol/dml/MessageManager.h | 40 ++++++++++++++++++ include/ki/protocol/dml/MessageModule.h | 51 +++++++++++++++++++++++ include/ki/protocol/dml/MessageTemplate.h | 35 ++++++++++++++++ 5 files changed, 202 insertions(+) create mode 100644 include/ki/protocol/dml/Message.h create mode 100644 include/ki/protocol/dml/MessageBuilder.h create mode 100644 include/ki/protocol/dml/MessageManager.h create mode 100644 include/ki/protocol/dml/MessageModule.h create mode 100644 include/ki/protocol/dml/MessageTemplate.h diff --git a/include/ki/protocol/dml/Message.h b/include/ki/protocol/dml/Message.h new file mode 100644 index 0000000..397b848 --- /dev/null +++ b/include/ki/protocol/dml/Message.h @@ -0,0 +1,38 @@ +#pragma once +#include "../../util/Serializable.h" +#include "../../dml/Record.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class Message : public util::Serializable + { + public: + Message(uint8_t service_id = 0, uint8_t type = 0); + virtual ~Message() = default; + + uint8_t get_service_id() const; + void set_service_id(uint8_t service_id); + + uint8_t get_type() const; + void set_type(uint8_t type); + + ki::dml::Record &get_record(); + const ki::dml::Record &get_record() const; + void set_record(const ki::dml::Record &record); + + void write_to(std::ostream &ostream) const final; + void read_from(std::istream &istream) final; + size_t get_size() const final; + private: + uint8_t m_service_id; + uint8_t m_type; + ki::dml::Record *m_record; + }; +} +} +} \ No newline at end of file diff --git a/include/ki/protocol/dml/MessageBuilder.h b/include/ki/protocol/dml/MessageBuilder.h new file mode 100644 index 0000000..2b2e134 --- /dev/null +++ b/include/ki/protocol/dml/MessageBuilder.h @@ -0,0 +1,38 @@ +#pragma once +#include "Message.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class MessageBuilder + { + public: + MessageBuilder(uint8_t service_id = 0, uint8_t type = 0); + + MessageBuilder &set_service_id(uint8_t service_id); + MessageBuilder &set_message_type(uint8_t type); + MessageBuilder &use_template_record(const ki::dml::Record &record); + + template + MessageBuilder &set_field_value(std::string name, ValueT value) + { + auto *field = m_message->get_record().get_field(name); + if (!field) + { + // TODO: Exceptions + } + field->set_value(value); + return *this; + } + + Message *get_message() const; + private: + Message *m_message; + }; +} +} +} \ No newline at end of file diff --git a/include/ki/protocol/dml/MessageManager.h b/include/ki/protocol/dml/MessageManager.h new file mode 100644 index 0000000..be3281e --- /dev/null +++ b/include/ki/protocol/dml/MessageManager.h @@ -0,0 +1,40 @@ +#pragma once +#include "MessageModule.h" +#include "MessageBuilder.h" +#include "../../dml/Record.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class MessageManager + { + public: + MessageManager(); + ~MessageManager(); + + static MessageManager &get_singleton(); + + const MessageModule &load_module(std::string filepath); + const MessageModule &get_module(uint8_t service_id) const; + const MessageModule &get_module(std::string protocol_type) const; + + MessageBuilder &build_message(uint8_t service_id, uint8_t message_type) const; + MessageBuilder &build_message(uint8_t service_id, std::string message_name) const; + MessageBuilder &build_message(std::string service_type, uint8_t message_type) const; + MessageBuilder &build_message(std::string service_type, std::string message_name) const; + + const Message *from_binary(std::istream &istream); + private: + static MessageManager *g_instance; + + MessageModuleList m_modules; + MessageModuleServiceIdMap m_service_id_map; + MessageModuleProtocolTypeMap m_protocol_type_map; + }; +} +} +} diff --git a/include/ki/protocol/dml/MessageModule.h b/include/ki/protocol/dml/MessageModule.h new file mode 100644 index 0000000..137cc42 --- /dev/null +++ b/include/ki/protocol/dml/MessageModule.h @@ -0,0 +1,51 @@ +#pragma once +#include "MessageTemplate.h" +#include +#include +#include +#include + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class MessageModule + { + public: + MessageModule(uint8_t service_id, std::string protocol_type); + ~MessageModule(); + + uint8_t get_service_id() const; + void set_service_id(uint8_t service_id); + + std::string get_protocol_type() const; + void set_protocol_type(std::string protocol_type); + + std::string get_protocol_desription() const; + void set_protocol_description(std::string protocol_description); + + const MessageTemplate &add_message_template(std::string name, uint8_t type, + ki::dml::Record *record); + const MessageTemplate &get_message_template(uint8_t type); + const MessageTemplate &get_message_template(std::string name); + + MessageBuilder &build_message(uint8_t message_type) const; + MessageBuilder &build_message(std::string message_name) const; + private: + uint8_t m_service_id; + std::string m_protocol_type; + std::string m_protocol_description; + + std::vector m_templates; + std::map m_message_name_map; + std::map m_message_type_map; + }; + + typedef std::vector MessageModuleList; + typedef std::map MessageModuleServiceIdMap; + typedef std::map MessageModuleProtocolTypeMap; +} +} +} diff --git a/include/ki/protocol/dml/MessageTemplate.h b/include/ki/protocol/dml/MessageTemplate.h new file mode 100644 index 0000000..ba8b744 --- /dev/null +++ b/include/ki/protocol/dml/MessageTemplate.h @@ -0,0 +1,35 @@ +#pragma once +#include "../../dml/Record.h" +#include "MessageBuilder.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class MessageTemplate + { + public: + MessageTemplate(std::string name, uint8_t type, ki::dml::Record *record); + ~MessageTemplate(); + + std::string get_name() const; + void set_name(std::string name); + + uint8_t get_type() const; + void set_type(uint8_t type); + + const ki::dml::Record &get_record() const; + void set_record(ki::dml::Record *record); + + MessageBuilder &build_message(); + private: + std::string m_name; + uint8_t m_type; + ki::dml::Record *m_record; + }; +} +} +} \ No newline at end of file From bd57a3db9d2ef0988156f87826ea0888067656b2 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Tue, 3 Apr 2018 16:43:00 +0100 Subject: [PATCH 04/49] protocol: Add source files to CMakeLists.txt also fix error in Packet.h --- CMakeLists.txt | 1 + include/ki/protocol/Packet.h | 3 ++- src/protocol/CMakeLists.txt | 12 +++++++++ src/protocol/Packet.cpp | 9 +++++++ src/protocol/control/ClientHello.cpp | 12 +++++++++ src/protocol/control/Ping.cpp | 12 +++++++++ src/protocol/control/ServerHello.cpp | 12 +++++++++ src/protocol/dml/Message.cpp | 12 +++++++++ src/protocol/dml/MessageBuilder.cpp | 38 ++++++++++++++++++++++++++++ src/protocol/dml/MessageManager.cpp | 12 +++++++++ src/protocol/dml/MessageModule.cpp | 12 +++++++++ src/protocol/dml/MessageTemplate.cpp | 12 +++++++++ 12 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/protocol/CMakeLists.txt create mode 100644 src/protocol/Packet.cpp create mode 100644 src/protocol/control/ClientHello.cpp create mode 100644 src/protocol/control/Ping.cpp create mode 100644 src/protocol/control/ServerHello.cpp create mode 100644 src/protocol/dml/Message.cpp create mode 100644 src/protocol/dml/MessageBuilder.cpp create mode 100644 src/protocol/dml/MessageManager.cpp create mode 100644 src/protocol/dml/MessageModule.cpp create mode 100644 src/protocol/dml/MessageTemplate.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9af9364..45f290e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ target_include_directories(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME} RapidXML) add_subdirectory("src/dml") +add_subdirectory("src/protocol") option(KI_BUILD_EXAMPLES "Determines whether to build examples." ON) if (KI_BUILD_EXAMPLES) diff --git a/include/ki/protocol/Packet.h b/include/ki/protocol/Packet.h index 79e2bc2..f49a8cd 100644 --- a/include/ki/protocol/Packet.h +++ b/include/ki/protocol/Packet.h @@ -29,7 +29,8 @@ namespace protocol std::ostringstream oss; data.write_to(oss); - m_payload = std::vector(oss.str().c_str(), data.get_size()); + std::string data_string = oss.str(); + m_payload.assign(data_string.begin(), data_string.end()); } template diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt new file mode 100644 index 0000000..2d6393a --- /dev/null +++ b/src/protocol/CMakeLists.txt @@ -0,0 +1,12 @@ +target_sources(${PROJECT_NAME} + PRIVATE + ${PROJECT_SOURCE_DIR}/src/protocol/Packet.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/control/ClientHello.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/control/ServerHello.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/control/Ping.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/Message.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageBuilder.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageManager.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageModule.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageTemplate.cpp +) \ No newline at end of file diff --git a/src/protocol/Packet.cpp b/src/protocol/Packet.cpp new file mode 100644 index 0000000..d78f078 --- /dev/null +++ b/src/protocol/Packet.cpp @@ -0,0 +1,9 @@ +#include "ki/protocol/Packet.h" + +namespace ki +{ +namespace protocol +{ + +} +} diff --git a/src/protocol/control/ClientHello.cpp b/src/protocol/control/ClientHello.cpp new file mode 100644 index 0000000..4edff91 --- /dev/null +++ b/src/protocol/control/ClientHello.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/control/ClientHello.h" + +namespace ki +{ +namespace protocol +{ +namespace control +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/control/Ping.cpp b/src/protocol/control/Ping.cpp new file mode 100644 index 0000000..7f6a0af --- /dev/null +++ b/src/protocol/control/Ping.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/control/Ping.h" + +namespace ki +{ +namespace protocol +{ +namespace control +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/control/ServerHello.cpp b/src/protocol/control/ServerHello.cpp new file mode 100644 index 0000000..8be2f86 --- /dev/null +++ b/src/protocol/control/ServerHello.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/control/ServerHello.h" + +namespace ki +{ +namespace protocol +{ +namespace control +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/dml/Message.cpp b/src/protocol/dml/Message.cpp new file mode 100644 index 0000000..d9a4dab --- /dev/null +++ b/src/protocol/dml/Message.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/dml/Message.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/dml/MessageBuilder.cpp b/src/protocol/dml/MessageBuilder.cpp new file mode 100644 index 0000000..aaed66f --- /dev/null +++ b/src/protocol/dml/MessageBuilder.cpp @@ -0,0 +1,38 @@ +#include "ki/protocol/dml/MessageBuilder.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + MessageBuilder::MessageBuilder(uint8_t service_id, uint8_t type) + { + m_message = new Message(service_id, type); + } + + MessageBuilder &MessageBuilder::set_service_id(uint8_t service_id) + { + m_message->set_service_id(service_id); + return *this; + } + + MessageBuilder &MessageBuilder::set_message_type(uint8_t type) + { + m_message->set_type(type); + return *this; + } + + MessageBuilder &MessageBuilder::use_template_record(const ki::dml::Record& record) + { + m_message->set_record(record); + return *this; + } + + Message *MessageBuilder::get_message() const + { + return m_message; + } +} +} +} \ No newline at end of file diff --git a/src/protocol/dml/MessageManager.cpp b/src/protocol/dml/MessageManager.cpp new file mode 100644 index 0000000..ae3703d --- /dev/null +++ b/src/protocol/dml/MessageManager.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/dml/MessageManager.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/dml/MessageModule.cpp b/src/protocol/dml/MessageModule.cpp new file mode 100644 index 0000000..9b239fc --- /dev/null +++ b/src/protocol/dml/MessageModule.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/dml/MessageModule.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/dml/MessageTemplate.cpp b/src/protocol/dml/MessageTemplate.cpp new file mode 100644 index 0000000..405de3f --- /dev/null +++ b/src/protocol/dml/MessageTemplate.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/dml/MessageTemplate.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + +} +} +} \ No newline at end of file From e0b5ca79233a7dd03bba3be118e36572ca7f41f0 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Tue, 3 Apr 2018 17:02:30 +0100 Subject: [PATCH 05/49] protocol: Add override specifier I thought final implied it --- include/ki/protocol/Packet.h | 8 +-- include/ki/protocol/control/ClientHello.h | 8 +-- include/ki/protocol/control/Ping.h | 76 +++++++++++------------ include/ki/protocol/control/ServerHello.h | 8 +-- include/ki/protocol/dml/Message.h | 8 +-- 5 files changed, 54 insertions(+), 54 deletions(-) diff --git a/include/ki/protocol/Packet.h b/include/ki/protocol/Packet.h index f49a8cd..e877c8b 100644 --- a/include/ki/protocol/Packet.h +++ b/include/ki/protocol/Packet.h @@ -9,7 +9,7 @@ namespace ki { namespace protocol { - class Packet : public util::Serializable + class Packet final : public util::Serializable { public: Packet(bool control = false, uint8_t opcode = 0); @@ -45,9 +45,9 @@ namespace protocol return data; } - virtual void write_to(std::ostream &ostream) const final; - virtual void read_from(std::istream &istream) final; - virtual size_t get_size() const final; + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; private: bool m_control; uint8_t m_opcode; diff --git a/include/ki/protocol/control/ClientHello.h b/include/ki/protocol/control/ClientHello.h index ee00b66..05b96ec 100644 --- a/include/ki/protocol/control/ClientHello.h +++ b/include/ki/protocol/control/ClientHello.h @@ -11,7 +11,7 @@ namespace protocol { namespace control { - class ClientHello : public util::Serializable + class ClientHello final : public util::Serializable { public: ClientHello(uint16_t session_id = 0, @@ -27,9 +27,9 @@ namespace control uint32_t get_milliseconds() const; void set_milliseconds(uint32_t milliseconds); - void write_to(std::ostream &ostream) const final; - void read_from(std::istream &istream) final; - size_t get_size() const final; + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; static Packet *create_packet(uint16_t session_id = 0, uint64_t timestamp = 0, uint32_t milliseconds = 0) diff --git a/include/ki/protocol/control/Ping.h b/include/ki/protocol/control/Ping.h index dff0ed9..494e38c 100644 --- a/include/ki/protocol/control/Ping.h +++ b/include/ki/protocol/control/Ping.h @@ -6,45 +6,45 @@ namespace ki { - namespace protocol +namespace protocol +{ +namespace control +{ + class Ping final : public util::Serializable { - namespace control + public: + Ping(uint16_t session_id = 0, + uint16_t milliseconds = 0, uint8_t minutes = 0); + virtual ~Ping() = default; + + uint16_t get_session_id() const; + void set_session_id(uint16_t session_id); + + uint16_t get_milliseconds() const; + void set_milliseconds(uint16_t milliseconds); + + uint8_t get_minutes() const; + void set_minutes(uint8_t minutes); + + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; + + static Packet *create_packet(uint16_t session_id = 0, + uint16_t milliseconds = 0, uint8_t minutes = 0, + bool response = false) { - class Ping : public util::Serializable - { - public: - Ping(uint16_t session_id = 0, - uint16_t milliseconds = 0, uint8_t minutes = 0); - virtual ~Ping() = default; - - uint16_t get_session_id() const; - void set_session_id(uint16_t session_id); - - uint16_t get_milliseconds() const; - void set_milliseconds(uint16_t milliseconds); - - uint8_t get_minutes() const; - void set_minutes(uint8_t minutes); - - void write_to(std::ostream &ostream) const final; - void read_from(std::istream &istream) final; - size_t get_size() const final; - - static Packet *create_packet(uint16_t session_id = 0, - uint16_t milliseconds = 0, uint8_t minutes = 0, - bool response = false) - { - Ping data(session_id, milliseconds, minutes); - auto *packet = new Packet(true, - (uint8_t)(response ? Opcode::PING_RSP : Opcode::PING)); - packet->set_payload_data(data); - return packet; - } - private: - uint16_t m_session_id; - uint16_t m_milliseconds; - uint8_t m_minutes; - }; + Ping data(session_id, milliseconds, minutes); + auto *packet = new Packet(true, + (uint8_t)(response ? Opcode::PING_RSP : Opcode::PING)); + packet->set_payload_data(data); + return packet; } - } + private: + uint16_t m_session_id; + uint16_t m_milliseconds; + uint8_t m_minutes; + }; +} +} } diff --git a/include/ki/protocol/control/ServerHello.h b/include/ki/protocol/control/ServerHello.h index 2c0d50e..123a7b6 100644 --- a/include/ki/protocol/control/ServerHello.h +++ b/include/ki/protocol/control/ServerHello.h @@ -11,7 +11,7 @@ namespace protocol { namespace control { - class ServerHello : public util::Serializable + class ServerHello final : public util::Serializable { public: ServerHello(uint16_t session_id = 0, @@ -27,9 +27,9 @@ namespace control uint32_t get_milliseconds() const; void set_milliseconds(uint32_t milliseconds); - void write_to(std::ostream &ostream) const final; - void read_from(std::istream &istream) final; - size_t get_size() const final; + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; static Packet *create_packet(uint16_t session_id = 0, uint64_t timestamp = 0, uint32_t milliseconds = 0) diff --git a/include/ki/protocol/dml/Message.h b/include/ki/protocol/dml/Message.h index 397b848..4709198 100644 --- a/include/ki/protocol/dml/Message.h +++ b/include/ki/protocol/dml/Message.h @@ -9,7 +9,7 @@ namespace protocol { namespace dml { - class Message : public util::Serializable + class Message final : public util::Serializable { public: Message(uint8_t service_id = 0, uint8_t type = 0); @@ -25,9 +25,9 @@ namespace dml const ki::dml::Record &get_record() const; void set_record(const ki::dml::Record &record); - void write_to(std::ostream &ostream) const final; - void read_from(std::istream &istream) final; - size_t get_size() const final; + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; private: uint8_t m_service_id; uint8_t m_type; From c9e485fa86cb32730f11d2355bb779cd52cf8e22 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 2 Apr 2018 01:24:32 +0100 Subject: [PATCH 06/49] protocol: Packet class definition --- include/ki/protocol/Packet.h | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 include/ki/protocol/Packet.h diff --git a/include/ki/protocol/Packet.h b/include/ki/protocol/Packet.h new file mode 100644 index 0000000..79e2bc2 --- /dev/null +++ b/include/ki/protocol/Packet.h @@ -0,0 +1,56 @@ +#pragma once +#include "../util/Serializable.h" +#include +#include +#include +#include + +namespace ki +{ +namespace protocol +{ + class Packet : public util::Serializable + { + public: + Packet(bool control = false, uint8_t opcode = 0); + virtual ~Packet() = default; + + bool is_control() const; + void set_control(bool control); + + uint8_t get_opcode() const; + void set_opcode(uint8_t opcode); + + template + void set_payload_data(const DataT &data) + { + static_assert(std::is_base_of::value, + "DataT must derive from Serializable."); + + std::ostringstream oss; + data.write_to(oss); + m_payload = std::vector(oss.str().c_str(), data.get_size()); + } + + template + DataT *get_payload_data() const + { + static_assert(std::is_base_of::value, + "DataT must derive from Serializable."); + + std::istringstream iss(m_payload.data()); + DataT *data = new DataT(); + data->read_from(iss); + return data; + } + + virtual void write_to(std::ostream &ostream) const final; + virtual void read_from(std::istream &istream) final; + virtual size_t get_size() const final; + private: + bool m_control; + uint8_t m_opcode; + std::vector m_payload; + }; +} +} From ac14fc5d743c7565c7a3da9485d9df2db35d53ef Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 2 Apr 2018 01:25:08 +0100 Subject: [PATCH 07/49] protocol: Definitions for control structures --- include/ki/protocol/control/ClientHello.h | 49 ++++++++++++++++++++++ include/ki/protocol/control/Opcode.h | 20 +++++++++ include/ki/protocol/control/Ping.h | 50 +++++++++++++++++++++++ include/ki/protocol/control/ServerHello.h | 49 ++++++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 include/ki/protocol/control/ClientHello.h create mode 100644 include/ki/protocol/control/Opcode.h create mode 100644 include/ki/protocol/control/Ping.h create mode 100644 include/ki/protocol/control/ServerHello.h diff --git a/include/ki/protocol/control/ClientHello.h b/include/ki/protocol/control/ClientHello.h new file mode 100644 index 0000000..ee00b66 --- /dev/null +++ b/include/ki/protocol/control/ClientHello.h @@ -0,0 +1,49 @@ +#pragma once +#include "../../util/Serializable.h" +#include "../Packet.h" +#include "Opcode.h" +#include +#include + +namespace ki +{ +namespace protocol +{ +namespace control +{ + class ClientHello : public util::Serializable + { + public: + ClientHello(uint16_t session_id = 0, + uint64_t timestamp = 0, uint32_t milliseconds = 0); + virtual ~ClientHello() = default; + + uint16_t get_session_id() const; + void set_session_id(uint16_t session_id); + + uint64_t get_timestamp() const; + void set_timestamp(uint64_t timestamp); + + uint32_t get_milliseconds() const; + void set_milliseconds(uint32_t milliseconds); + + void write_to(std::ostream &ostream) const final; + void read_from(std::istream &istream) final; + size_t get_size() const final; + + static Packet *create_packet(uint16_t session_id = 0, + uint64_t timestamp = 0, uint32_t milliseconds = 0) + { + ClientHello data(session_id, timestamp, milliseconds); + auto *packet = new Packet(true, (uint8_t)Opcode::CLIENT_HELLO); + packet->set_payload_data(data); + return packet; + } + private: + uint16_t m_session_id; + uint64_t m_timestamp; + uint32_t m_milliseconds; + }; +} +} +} diff --git a/include/ki/protocol/control/Opcode.h b/include/ki/protocol/control/Opcode.h new file mode 100644 index 0000000..8442a56 --- /dev/null +++ b/include/ki/protocol/control/Opcode.h @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace ki +{ +namespace protocol +{ +namespace control +{ + enum class Opcode : uint8_t + { + SERVER_HELLO = 0, + UDP_HELLO = 1, + PING = 3, + PING_RSP = 4, + CLIENT_HELLO = 5 + }; +} +} +} \ No newline at end of file diff --git a/include/ki/protocol/control/Ping.h b/include/ki/protocol/control/Ping.h new file mode 100644 index 0000000..dff0ed9 --- /dev/null +++ b/include/ki/protocol/control/Ping.h @@ -0,0 +1,50 @@ +#pragma once +#include "../../util/Serializable.h" +#include "../Packet.h" +#include "Opcode.h" +#include + +namespace ki +{ + namespace protocol + { + namespace control + { + class Ping : public util::Serializable + { + public: + Ping(uint16_t session_id = 0, + uint16_t milliseconds = 0, uint8_t minutes = 0); + virtual ~Ping() = default; + + uint16_t get_session_id() const; + void set_session_id(uint16_t session_id); + + uint16_t get_milliseconds() const; + void set_milliseconds(uint16_t milliseconds); + + uint8_t get_minutes() const; + void set_minutes(uint8_t minutes); + + void write_to(std::ostream &ostream) const final; + void read_from(std::istream &istream) final; + size_t get_size() const final; + + static Packet *create_packet(uint16_t session_id = 0, + uint16_t milliseconds = 0, uint8_t minutes = 0, + bool response = false) + { + Ping data(session_id, milliseconds, minutes); + auto *packet = new Packet(true, + (uint8_t)(response ? Opcode::PING_RSP : Opcode::PING)); + packet->set_payload_data(data); + return packet; + } + private: + uint16_t m_session_id; + uint16_t m_milliseconds; + uint8_t m_minutes; + }; + } + } +} diff --git a/include/ki/protocol/control/ServerHello.h b/include/ki/protocol/control/ServerHello.h new file mode 100644 index 0000000..2c0d50e --- /dev/null +++ b/include/ki/protocol/control/ServerHello.h @@ -0,0 +1,49 @@ +#pragma once +#include "../../util/Serializable.h" +#include "../Packet.h" +#include "Opcode.h" +#include +#include + +namespace ki +{ +namespace protocol +{ +namespace control +{ + class ServerHello : public util::Serializable + { + public: + ServerHello(uint16_t session_id = 0, + uint64_t timestamp = 0, uint32_t milliseconds = 0); + virtual ~ServerHello() = default; + + uint16_t get_session_id() const; + void set_session_id(uint16_t session_id); + + uint64_t get_timestamp() const; + void set_timestamp(uint64_t timestamp); + + uint32_t get_milliseconds() const; + void set_milliseconds(uint32_t milliseconds); + + void write_to(std::ostream &ostream) const final; + void read_from(std::istream &istream) final; + size_t get_size() const final; + + static Packet *create_packet(uint16_t session_id = 0, + uint64_t timestamp = 0, uint32_t milliseconds = 0) + { + ServerHello data(session_id, timestamp, milliseconds); + auto *packet = new Packet(true, (uint8_t)Opcode::SERVER_HELLO); + packet->set_payload_data(data); + return packet; + } + private: + uint16_t m_session_id; + uint64_t m_timestamp; + uint32_t m_milliseconds; + }; +} +} +} From bfe56f194a65060ca8f6c9b00d8c61c68784511f Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Tue, 3 Apr 2018 16:11:06 +0100 Subject: [PATCH 08/49] protocol: Definitions for DML messages --- include/ki/protocol/dml/Message.h | 38 +++++++++++++++++ include/ki/protocol/dml/MessageBuilder.h | 38 +++++++++++++++++ include/ki/protocol/dml/MessageManager.h | 40 ++++++++++++++++++ include/ki/protocol/dml/MessageModule.h | 51 +++++++++++++++++++++++ include/ki/protocol/dml/MessageTemplate.h | 35 ++++++++++++++++ 5 files changed, 202 insertions(+) create mode 100644 include/ki/protocol/dml/Message.h create mode 100644 include/ki/protocol/dml/MessageBuilder.h create mode 100644 include/ki/protocol/dml/MessageManager.h create mode 100644 include/ki/protocol/dml/MessageModule.h create mode 100644 include/ki/protocol/dml/MessageTemplate.h diff --git a/include/ki/protocol/dml/Message.h b/include/ki/protocol/dml/Message.h new file mode 100644 index 0000000..397b848 --- /dev/null +++ b/include/ki/protocol/dml/Message.h @@ -0,0 +1,38 @@ +#pragma once +#include "../../util/Serializable.h" +#include "../../dml/Record.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class Message : public util::Serializable + { + public: + Message(uint8_t service_id = 0, uint8_t type = 0); + virtual ~Message() = default; + + uint8_t get_service_id() const; + void set_service_id(uint8_t service_id); + + uint8_t get_type() const; + void set_type(uint8_t type); + + ki::dml::Record &get_record(); + const ki::dml::Record &get_record() const; + void set_record(const ki::dml::Record &record); + + void write_to(std::ostream &ostream) const final; + void read_from(std::istream &istream) final; + size_t get_size() const final; + private: + uint8_t m_service_id; + uint8_t m_type; + ki::dml::Record *m_record; + }; +} +} +} \ No newline at end of file diff --git a/include/ki/protocol/dml/MessageBuilder.h b/include/ki/protocol/dml/MessageBuilder.h new file mode 100644 index 0000000..2b2e134 --- /dev/null +++ b/include/ki/protocol/dml/MessageBuilder.h @@ -0,0 +1,38 @@ +#pragma once +#include "Message.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class MessageBuilder + { + public: + MessageBuilder(uint8_t service_id = 0, uint8_t type = 0); + + MessageBuilder &set_service_id(uint8_t service_id); + MessageBuilder &set_message_type(uint8_t type); + MessageBuilder &use_template_record(const ki::dml::Record &record); + + template + MessageBuilder &set_field_value(std::string name, ValueT value) + { + auto *field = m_message->get_record().get_field(name); + if (!field) + { + // TODO: Exceptions + } + field->set_value(value); + return *this; + } + + Message *get_message() const; + private: + Message *m_message; + }; +} +} +} \ No newline at end of file diff --git a/include/ki/protocol/dml/MessageManager.h b/include/ki/protocol/dml/MessageManager.h new file mode 100644 index 0000000..be3281e --- /dev/null +++ b/include/ki/protocol/dml/MessageManager.h @@ -0,0 +1,40 @@ +#pragma once +#include "MessageModule.h" +#include "MessageBuilder.h" +#include "../../dml/Record.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class MessageManager + { + public: + MessageManager(); + ~MessageManager(); + + static MessageManager &get_singleton(); + + const MessageModule &load_module(std::string filepath); + const MessageModule &get_module(uint8_t service_id) const; + const MessageModule &get_module(std::string protocol_type) const; + + MessageBuilder &build_message(uint8_t service_id, uint8_t message_type) const; + MessageBuilder &build_message(uint8_t service_id, std::string message_name) const; + MessageBuilder &build_message(std::string service_type, uint8_t message_type) const; + MessageBuilder &build_message(std::string service_type, std::string message_name) const; + + const Message *from_binary(std::istream &istream); + private: + static MessageManager *g_instance; + + MessageModuleList m_modules; + MessageModuleServiceIdMap m_service_id_map; + MessageModuleProtocolTypeMap m_protocol_type_map; + }; +} +} +} diff --git a/include/ki/protocol/dml/MessageModule.h b/include/ki/protocol/dml/MessageModule.h new file mode 100644 index 0000000..137cc42 --- /dev/null +++ b/include/ki/protocol/dml/MessageModule.h @@ -0,0 +1,51 @@ +#pragma once +#include "MessageTemplate.h" +#include +#include +#include +#include + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class MessageModule + { + public: + MessageModule(uint8_t service_id, std::string protocol_type); + ~MessageModule(); + + uint8_t get_service_id() const; + void set_service_id(uint8_t service_id); + + std::string get_protocol_type() const; + void set_protocol_type(std::string protocol_type); + + std::string get_protocol_desription() const; + void set_protocol_description(std::string protocol_description); + + const MessageTemplate &add_message_template(std::string name, uint8_t type, + ki::dml::Record *record); + const MessageTemplate &get_message_template(uint8_t type); + const MessageTemplate &get_message_template(std::string name); + + MessageBuilder &build_message(uint8_t message_type) const; + MessageBuilder &build_message(std::string message_name) const; + private: + uint8_t m_service_id; + std::string m_protocol_type; + std::string m_protocol_description; + + std::vector m_templates; + std::map m_message_name_map; + std::map m_message_type_map; + }; + + typedef std::vector MessageModuleList; + typedef std::map MessageModuleServiceIdMap; + typedef std::map MessageModuleProtocolTypeMap; +} +} +} diff --git a/include/ki/protocol/dml/MessageTemplate.h b/include/ki/protocol/dml/MessageTemplate.h new file mode 100644 index 0000000..ba8b744 --- /dev/null +++ b/include/ki/protocol/dml/MessageTemplate.h @@ -0,0 +1,35 @@ +#pragma once +#include "../../dml/Record.h" +#include "MessageBuilder.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class MessageTemplate + { + public: + MessageTemplate(std::string name, uint8_t type, ki::dml::Record *record); + ~MessageTemplate(); + + std::string get_name() const; + void set_name(std::string name); + + uint8_t get_type() const; + void set_type(uint8_t type); + + const ki::dml::Record &get_record() const; + void set_record(ki::dml::Record *record); + + MessageBuilder &build_message(); + private: + std::string m_name; + uint8_t m_type; + ki::dml::Record *m_record; + }; +} +} +} \ No newline at end of file From 85361f0e40523acf757aa7f0e17116410bf58543 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Tue, 3 Apr 2018 16:43:00 +0100 Subject: [PATCH 09/49] protocol: Add source files to CMakeLists.txt also fix error in Packet.h --- CMakeLists.txt | 1 + include/ki/protocol/Packet.h | 3 ++- src/protocol/CMakeLists.txt | 12 +++++++++ src/protocol/Packet.cpp | 9 +++++++ src/protocol/control/ClientHello.cpp | 12 +++++++++ src/protocol/control/Ping.cpp | 12 +++++++++ src/protocol/control/ServerHello.cpp | 12 +++++++++ src/protocol/dml/Message.cpp | 12 +++++++++ src/protocol/dml/MessageBuilder.cpp | 38 ++++++++++++++++++++++++++++ src/protocol/dml/MessageManager.cpp | 12 +++++++++ src/protocol/dml/MessageModule.cpp | 12 +++++++++ src/protocol/dml/MessageTemplate.cpp | 12 +++++++++ 12 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/protocol/CMakeLists.txt create mode 100644 src/protocol/Packet.cpp create mode 100644 src/protocol/control/ClientHello.cpp create mode 100644 src/protocol/control/Ping.cpp create mode 100644 src/protocol/control/ServerHello.cpp create mode 100644 src/protocol/dml/Message.cpp create mode 100644 src/protocol/dml/MessageBuilder.cpp create mode 100644 src/protocol/dml/MessageManager.cpp create mode 100644 src/protocol/dml/MessageModule.cpp create mode 100644 src/protocol/dml/MessageTemplate.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9af9364..45f290e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ target_include_directories(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME} RapidXML) add_subdirectory("src/dml") +add_subdirectory("src/protocol") option(KI_BUILD_EXAMPLES "Determines whether to build examples." ON) if (KI_BUILD_EXAMPLES) diff --git a/include/ki/protocol/Packet.h b/include/ki/protocol/Packet.h index 79e2bc2..f49a8cd 100644 --- a/include/ki/protocol/Packet.h +++ b/include/ki/protocol/Packet.h @@ -29,7 +29,8 @@ namespace protocol std::ostringstream oss; data.write_to(oss); - m_payload = std::vector(oss.str().c_str(), data.get_size()); + std::string data_string = oss.str(); + m_payload.assign(data_string.begin(), data_string.end()); } template diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt new file mode 100644 index 0000000..2d6393a --- /dev/null +++ b/src/protocol/CMakeLists.txt @@ -0,0 +1,12 @@ +target_sources(${PROJECT_NAME} + PRIVATE + ${PROJECT_SOURCE_DIR}/src/protocol/Packet.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/control/ClientHello.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/control/ServerHello.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/control/Ping.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/Message.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageBuilder.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageManager.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageModule.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageTemplate.cpp +) \ No newline at end of file diff --git a/src/protocol/Packet.cpp b/src/protocol/Packet.cpp new file mode 100644 index 0000000..d78f078 --- /dev/null +++ b/src/protocol/Packet.cpp @@ -0,0 +1,9 @@ +#include "ki/protocol/Packet.h" + +namespace ki +{ +namespace protocol +{ + +} +} diff --git a/src/protocol/control/ClientHello.cpp b/src/protocol/control/ClientHello.cpp new file mode 100644 index 0000000..4edff91 --- /dev/null +++ b/src/protocol/control/ClientHello.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/control/ClientHello.h" + +namespace ki +{ +namespace protocol +{ +namespace control +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/control/Ping.cpp b/src/protocol/control/Ping.cpp new file mode 100644 index 0000000..7f6a0af --- /dev/null +++ b/src/protocol/control/Ping.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/control/Ping.h" + +namespace ki +{ +namespace protocol +{ +namespace control +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/control/ServerHello.cpp b/src/protocol/control/ServerHello.cpp new file mode 100644 index 0000000..8be2f86 --- /dev/null +++ b/src/protocol/control/ServerHello.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/control/ServerHello.h" + +namespace ki +{ +namespace protocol +{ +namespace control +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/dml/Message.cpp b/src/protocol/dml/Message.cpp new file mode 100644 index 0000000..d9a4dab --- /dev/null +++ b/src/protocol/dml/Message.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/dml/Message.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/dml/MessageBuilder.cpp b/src/protocol/dml/MessageBuilder.cpp new file mode 100644 index 0000000..aaed66f --- /dev/null +++ b/src/protocol/dml/MessageBuilder.cpp @@ -0,0 +1,38 @@ +#include "ki/protocol/dml/MessageBuilder.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + MessageBuilder::MessageBuilder(uint8_t service_id, uint8_t type) + { + m_message = new Message(service_id, type); + } + + MessageBuilder &MessageBuilder::set_service_id(uint8_t service_id) + { + m_message->set_service_id(service_id); + return *this; + } + + MessageBuilder &MessageBuilder::set_message_type(uint8_t type) + { + m_message->set_type(type); + return *this; + } + + MessageBuilder &MessageBuilder::use_template_record(const ki::dml::Record& record) + { + m_message->set_record(record); + return *this; + } + + Message *MessageBuilder::get_message() const + { + return m_message; + } +} +} +} \ No newline at end of file diff --git a/src/protocol/dml/MessageManager.cpp b/src/protocol/dml/MessageManager.cpp new file mode 100644 index 0000000..ae3703d --- /dev/null +++ b/src/protocol/dml/MessageManager.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/dml/MessageManager.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/dml/MessageModule.cpp b/src/protocol/dml/MessageModule.cpp new file mode 100644 index 0000000..9b239fc --- /dev/null +++ b/src/protocol/dml/MessageModule.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/dml/MessageModule.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + +} +} +} \ No newline at end of file diff --git a/src/protocol/dml/MessageTemplate.cpp b/src/protocol/dml/MessageTemplate.cpp new file mode 100644 index 0000000..405de3f --- /dev/null +++ b/src/protocol/dml/MessageTemplate.cpp @@ -0,0 +1,12 @@ +#include "ki/protocol/dml/MessageTemplate.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + +} +} +} \ No newline at end of file From 0268cf247df07ae17eb1354c20a8a22addfdf313 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Tue, 3 Apr 2018 17:02:30 +0100 Subject: [PATCH 10/49] protocol: Add override specifier I thought final implied it --- include/ki/protocol/Packet.h | 8 +-- include/ki/protocol/control/ClientHello.h | 8 +-- include/ki/protocol/control/Ping.h | 76 +++++++++++------------ include/ki/protocol/control/ServerHello.h | 8 +-- include/ki/protocol/dml/Message.h | 8 +-- 5 files changed, 54 insertions(+), 54 deletions(-) diff --git a/include/ki/protocol/Packet.h b/include/ki/protocol/Packet.h index f49a8cd..e877c8b 100644 --- a/include/ki/protocol/Packet.h +++ b/include/ki/protocol/Packet.h @@ -9,7 +9,7 @@ namespace ki { namespace protocol { - class Packet : public util::Serializable + class Packet final : public util::Serializable { public: Packet(bool control = false, uint8_t opcode = 0); @@ -45,9 +45,9 @@ namespace protocol return data; } - virtual void write_to(std::ostream &ostream) const final; - virtual void read_from(std::istream &istream) final; - virtual size_t get_size() const final; + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; private: bool m_control; uint8_t m_opcode; diff --git a/include/ki/protocol/control/ClientHello.h b/include/ki/protocol/control/ClientHello.h index ee00b66..05b96ec 100644 --- a/include/ki/protocol/control/ClientHello.h +++ b/include/ki/protocol/control/ClientHello.h @@ -11,7 +11,7 @@ namespace protocol { namespace control { - class ClientHello : public util::Serializable + class ClientHello final : public util::Serializable { public: ClientHello(uint16_t session_id = 0, @@ -27,9 +27,9 @@ namespace control uint32_t get_milliseconds() const; void set_milliseconds(uint32_t milliseconds); - void write_to(std::ostream &ostream) const final; - void read_from(std::istream &istream) final; - size_t get_size() const final; + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; static Packet *create_packet(uint16_t session_id = 0, uint64_t timestamp = 0, uint32_t milliseconds = 0) diff --git a/include/ki/protocol/control/Ping.h b/include/ki/protocol/control/Ping.h index dff0ed9..494e38c 100644 --- a/include/ki/protocol/control/Ping.h +++ b/include/ki/protocol/control/Ping.h @@ -6,45 +6,45 @@ namespace ki { - namespace protocol +namespace protocol +{ +namespace control +{ + class Ping final : public util::Serializable { - namespace control + public: + Ping(uint16_t session_id = 0, + uint16_t milliseconds = 0, uint8_t minutes = 0); + virtual ~Ping() = default; + + uint16_t get_session_id() const; + void set_session_id(uint16_t session_id); + + uint16_t get_milliseconds() const; + void set_milliseconds(uint16_t milliseconds); + + uint8_t get_minutes() const; + void set_minutes(uint8_t minutes); + + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; + + static Packet *create_packet(uint16_t session_id = 0, + uint16_t milliseconds = 0, uint8_t minutes = 0, + bool response = false) { - class Ping : public util::Serializable - { - public: - Ping(uint16_t session_id = 0, - uint16_t milliseconds = 0, uint8_t minutes = 0); - virtual ~Ping() = default; - - uint16_t get_session_id() const; - void set_session_id(uint16_t session_id); - - uint16_t get_milliseconds() const; - void set_milliseconds(uint16_t milliseconds); - - uint8_t get_minutes() const; - void set_minutes(uint8_t minutes); - - void write_to(std::ostream &ostream) const final; - void read_from(std::istream &istream) final; - size_t get_size() const final; - - static Packet *create_packet(uint16_t session_id = 0, - uint16_t milliseconds = 0, uint8_t minutes = 0, - bool response = false) - { - Ping data(session_id, milliseconds, minutes); - auto *packet = new Packet(true, - (uint8_t)(response ? Opcode::PING_RSP : Opcode::PING)); - packet->set_payload_data(data); - return packet; - } - private: - uint16_t m_session_id; - uint16_t m_milliseconds; - uint8_t m_minutes; - }; + Ping data(session_id, milliseconds, minutes); + auto *packet = new Packet(true, + (uint8_t)(response ? Opcode::PING_RSP : Opcode::PING)); + packet->set_payload_data(data); + return packet; } - } + private: + uint16_t m_session_id; + uint16_t m_milliseconds; + uint8_t m_minutes; + }; +} +} } diff --git a/include/ki/protocol/control/ServerHello.h b/include/ki/protocol/control/ServerHello.h index 2c0d50e..123a7b6 100644 --- a/include/ki/protocol/control/ServerHello.h +++ b/include/ki/protocol/control/ServerHello.h @@ -11,7 +11,7 @@ namespace protocol { namespace control { - class ServerHello : public util::Serializable + class ServerHello final : public util::Serializable { public: ServerHello(uint16_t session_id = 0, @@ -27,9 +27,9 @@ namespace control uint32_t get_milliseconds() const; void set_milliseconds(uint32_t milliseconds); - void write_to(std::ostream &ostream) const final; - void read_from(std::istream &istream) final; - size_t get_size() const final; + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; static Packet *create_packet(uint16_t session_id = 0, uint64_t timestamp = 0, uint32_t milliseconds = 0) diff --git a/include/ki/protocol/dml/Message.h b/include/ki/protocol/dml/Message.h index 397b848..4709198 100644 --- a/include/ki/protocol/dml/Message.h +++ b/include/ki/protocol/dml/Message.h @@ -9,7 +9,7 @@ namespace protocol { namespace dml { - class Message : public util::Serializable + class Message final : public util::Serializable { public: Message(uint8_t service_id = 0, uint8_t type = 0); @@ -25,9 +25,9 @@ namespace dml const ki::dml::Record &get_record() const; void set_record(const ki::dml::Record &record); - void write_to(std::ostream &ostream) const final; - void read_from(std::istream &istream) final; - size_t get_size() const final; + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; private: uint8_t m_service_id; uint8_t m_type; From 9653475ed92bc9c7373eab1eca5d3f6df59ba190 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Tue, 3 Apr 2018 19:49:58 +0100 Subject: [PATCH 11/49] protocol: Implement Packet --- src/protocol/Packet.cpp | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/protocol/Packet.cpp b/src/protocol/Packet.cpp index d78f078..1223793 100644 --- a/src/protocol/Packet.cpp +++ b/src/protocol/Packet.cpp @@ -4,6 +4,60 @@ namespace ki { namespace protocol { + Packet::Packet(bool control, uint8_t opcode) + { + m_control = control; + m_opcode = opcode; + m_payload = std::vector(); + } + bool Packet::is_control() const + { + return m_control; + } + + void Packet::set_control(bool control) + { + m_control = control; + } + + uint8_t Packet::get_opcode() const + { + return m_opcode; + } + + void Packet::set_opcode(uint8_t opcode) + { + m_opcode = opcode; + } + + void Packet::write_to(std::ostream& ostream) const + { + ostream.put(m_control); + ostream.put(m_opcode); + ostream.put(0); + ostream.put(0); + ostream.write(m_payload.data(), m_payload.size()); + } + + void Packet::read_from(std::istream& istream) + { + m_control = istream.get() >= 1; + m_opcode = istream.get(); + istream.ignore(2); + + const std::ios::pos_type pos = istream.tellg(); + istream.seekg(0, std::ios::end); + const size_t size = istream.tellg() - pos; + istream.seekg(pos, std::ios::beg); + + m_payload.resize(size); + istream.read(m_payload.data(), size); + } + + size_t Packet::get_size() const + { + return 4 + m_payload.size(); + } } } From 915b6e7e0af886e3aad85cfc6319873210a87646 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Tue, 3 Apr 2018 21:36:24 +0100 Subject: [PATCH 12/49] protocol: Implement control messages --- include/ki/protocol/control/ClientHello.h | 16 ++--- include/ki/protocol/control/Ping.h | 9 +-- include/ki/protocol/control/ServerHello.h | 16 ++--- src/protocol/control/ClientHello.cpp | 79 +++++++++++++++++++++++ src/protocol/control/Ping.cpp | 74 +++++++++++++++++++++ src/protocol/control/ServerHello.cpp | 76 ++++++++++++++++++++++ 6 files changed, 240 insertions(+), 30 deletions(-) diff --git a/include/ki/protocol/control/ClientHello.h b/include/ki/protocol/control/ClientHello.h index 05b96ec..f126d8c 100644 --- a/include/ki/protocol/control/ClientHello.h +++ b/include/ki/protocol/control/ClientHello.h @@ -15,14 +15,14 @@ namespace control { public: ClientHello(uint16_t session_id = 0, - uint64_t timestamp = 0, uint32_t milliseconds = 0); + uint32_t timestamp = 0, uint32_t milliseconds = 0); virtual ~ClientHello() = default; uint16_t get_session_id() const; void set_session_id(uint16_t session_id); - uint64_t get_timestamp() const; - void set_timestamp(uint64_t timestamp); + uint32_t get_timestamp() const; + void set_timestamp(uint32_t timestamp); uint32_t get_milliseconds() const; void set_milliseconds(uint32_t milliseconds); @@ -32,16 +32,10 @@ namespace control size_t get_size() const override final; static Packet *create_packet(uint16_t session_id = 0, - uint64_t timestamp = 0, uint32_t milliseconds = 0) - { - ClientHello data(session_id, timestamp, milliseconds); - auto *packet = new Packet(true, (uint8_t)Opcode::CLIENT_HELLO); - packet->set_payload_data(data); - return packet; - } + uint32_t timestamp = 0, uint32_t milliseconds = 0); private: uint16_t m_session_id; - uint64_t m_timestamp; + uint32_t m_timestamp; uint32_t m_milliseconds; }; } diff --git a/include/ki/protocol/control/Ping.h b/include/ki/protocol/control/Ping.h index 494e38c..5ab0b33 100644 --- a/include/ki/protocol/control/Ping.h +++ b/include/ki/protocol/control/Ping.h @@ -32,14 +32,7 @@ namespace control static Packet *create_packet(uint16_t session_id = 0, uint16_t milliseconds = 0, uint8_t minutes = 0, - bool response = false) - { - Ping data(session_id, milliseconds, minutes); - auto *packet = new Packet(true, - (uint8_t)(response ? Opcode::PING_RSP : Opcode::PING)); - packet->set_payload_data(data); - return packet; - } + bool response = false); private: uint16_t m_session_id; uint16_t m_milliseconds; diff --git a/include/ki/protocol/control/ServerHello.h b/include/ki/protocol/control/ServerHello.h index 123a7b6..f94106b 100644 --- a/include/ki/protocol/control/ServerHello.h +++ b/include/ki/protocol/control/ServerHello.h @@ -15,14 +15,14 @@ namespace control { public: ServerHello(uint16_t session_id = 0, - uint64_t timestamp = 0, uint32_t milliseconds = 0); + uint32_t timestamp = 0, uint32_t milliseconds = 0); virtual ~ServerHello() = default; uint16_t get_session_id() const; void set_session_id(uint16_t session_id); - uint64_t get_timestamp() const; - void set_timestamp(uint64_t timestamp); + uint32_t get_timestamp() const; + void set_timestamp(uint32_t timestamp); uint32_t get_milliseconds() const; void set_milliseconds(uint32_t milliseconds); @@ -32,16 +32,10 @@ namespace control size_t get_size() const override final; static Packet *create_packet(uint16_t session_id = 0, - uint64_t timestamp = 0, uint32_t milliseconds = 0) - { - ServerHello data(session_id, timestamp, milliseconds); - auto *packet = new Packet(true, (uint8_t)Opcode::SERVER_HELLO); - packet->set_payload_data(data); - return packet; - } + uint32_t timestamp = 0, uint32_t milliseconds = 0); private: uint16_t m_session_id; - uint64_t m_timestamp; + uint32_t m_timestamp; uint32_t m_milliseconds; }; } diff --git a/src/protocol/control/ClientHello.cpp b/src/protocol/control/ClientHello.cpp index 4edff91..50b7cd9 100644 --- a/src/protocol/control/ClientHello.cpp +++ b/src/protocol/control/ClientHello.cpp @@ -1,4 +1,5 @@ #include "ki/protocol/control/ClientHello.h" +#include "ki/dml/Record.h" namespace ki { @@ -6,7 +7,85 @@ namespace protocol { namespace control { + ClientHello::ClientHello(uint16_t session_id, + uint32_t timestamp, uint32_t milliseconds) + { + m_session_id = session_id; + m_timestamp = timestamp; + m_milliseconds = milliseconds; + } + uint16_t ClientHello::get_session_id() const + { + return m_session_id; + } + + void ClientHello::set_session_id(uint16_t session_id) + { + m_session_id = session_id; + } + + uint32_t ClientHello::get_timestamp() const + { + return m_timestamp; + } + + void ClientHello::set_timestamp(uint32_t timestamp) + { + m_timestamp = timestamp; + } + + uint32_t ClientHello::get_milliseconds() const + { + return m_milliseconds; + } + + void ClientHello::set_milliseconds(uint32_t milliseconds) + { + m_milliseconds = milliseconds; + } + + void ClientHello::write_to(std::ostream& ostream) const + { + dml::Record record; + record.add_field("unknown"); + record.add_field("unknown2"); + record.add_field("m_timestamp")->set_value(m_timestamp); + record.add_field("m_milliseconds")->set_value(m_milliseconds); + record.add_field("m_session_id")->set_value(m_session_id); + record.write_to(ostream); + } + + void ClientHello::read_from(std::istream& istream) + { + dml::Record record; + record.add_field("unknown"); + record.add_field("unknown2"); + auto *timestamp = record.add_field("m_timestamp"); + auto *milliseconds = record.add_field("m_milliseconds"); + auto *session_id = record.add_field("m_session_id"); + record.read_from(istream); + + m_timestamp = timestamp->get_value(); + m_milliseconds = milliseconds->get_value(); + m_session_id = session_id->get_value(); + } + + size_t ClientHello::get_size() const + { + return sizeof(dml::USHRT) + sizeof(dml::UINT) + + sizeof(dml::UINT) + sizeof(dml::UINT) + + sizeof(dml::USHRT); + } + + Packet *ClientHello::create_packet(uint16_t session_id, + uint32_t timestamp, uint32_t milliseconds) + { + const ClientHello data(session_id, timestamp, milliseconds); + auto *packet = new Packet(true, (uint8_t)Opcode::CLIENT_HELLO); + packet->set_payload_data(data); + return packet; + } } } } \ No newline at end of file diff --git a/src/protocol/control/Ping.cpp b/src/protocol/control/Ping.cpp index 7f6a0af..b5a2cd5 100644 --- a/src/protocol/control/Ping.cpp +++ b/src/protocol/control/Ping.cpp @@ -1,4 +1,5 @@ #include "ki/protocol/control/Ping.h" +#include "ki/dml/Record.h" namespace ki { @@ -6,7 +7,80 @@ namespace protocol { namespace control { + Ping::Ping(uint16_t session_id, uint16_t milliseconds, uint8_t minutes) + { + m_session_id = session_id; + m_milliseconds = milliseconds; + m_minutes = minutes; + } + uint16_t Ping::get_session_id() const + { + return m_session_id; + } + + void Ping::set_session_id(uint16_t session_id) + { + m_session_id = session_id; + } + + uint16_t Ping::get_milliseconds() const + { + return m_milliseconds; + } + + void Ping::set_milliseconds(uint16_t milliseconds) + { + m_milliseconds = milliseconds; + } + + uint8_t Ping::get_minutes() const + { + return m_minutes; + } + + void Ping::set_minutes(uint8_t minutes) + { + m_minutes = minutes; + } + + void Ping::write_to(std::ostream &ostream) const + { + dml::Record record; + record.add_field("m_session_id")->set_value(m_session_id); + record.add_field("m_milliseconds")->set_value(m_milliseconds); + record.add_field("m_minutes")->set_value(m_minutes); + record.write_to(ostream); + } + + void Ping::read_from(std::istream &istream) + { + dml::Record record; + auto *session_id = record.add_field("m_session_id"); + auto *milliseconds = record.add_field("m_milliseconds"); + auto *minutes = record.add_field("m_minutes"); + record.read_from(istream); + + m_session_id = session_id->get_value(); + m_milliseconds = milliseconds->get_value(); + m_minutes = minutes->get_value(); + } + + size_t Ping::get_size() const + { + return sizeof(dml::USHRT) + sizeof(dml::USHRT) + + sizeof(dml::UBYT); + } + + Packet* Ping::create_packet(uint16_t session_id, + uint16_t milliseconds, uint8_t minutes, bool response) + { + const Ping data(session_id, milliseconds, minutes); + auto *packet = new Packet(true, + (uint8_t)(response ? Opcode::PING_RSP : Opcode::PING)); + packet->set_payload_data(data); + return packet; + } } } } \ No newline at end of file diff --git a/src/protocol/control/ServerHello.cpp b/src/protocol/control/ServerHello.cpp index 8be2f86..4be2c91 100644 --- a/src/protocol/control/ServerHello.cpp +++ b/src/protocol/control/ServerHello.cpp @@ -1,4 +1,5 @@ #include "ki/protocol/control/ServerHello.h" +#include "ki/dml/Record.h" namespace ki { @@ -6,7 +7,82 @@ namespace protocol { namespace control { + ServerHello::ServerHello(uint16_t session_id, + uint32_t timestamp, uint32_t milliseconds) + { + m_session_id = session_id; + m_timestamp = timestamp; + m_milliseconds = milliseconds; + } + uint16_t ServerHello::get_session_id() const + { + return m_session_id; + } + + void ServerHello::set_session_id(uint16_t session_id) + { + m_session_id = session_id; + } + + uint32_t ServerHello::get_timestamp() const + { + return m_timestamp; + } + + void ServerHello::set_timestamp(uint32_t timestamp) + { + m_timestamp = timestamp; + } + + uint32_t ServerHello::get_milliseconds() const + { + return m_milliseconds; + } + + void ServerHello::set_milliseconds(uint32_t milliseconds) + { + m_milliseconds = milliseconds; + } + + void ServerHello::write_to(std::ostream& ostream) const + { + dml::Record record; + record.add_field("m_session_id")->set_value(m_session_id); + record.add_field("unknown"); + record.add_field("m_timestamp")->set_value(m_timestamp); + record.add_field("m_milliseconds")->set_value(m_milliseconds); + record.write_to(ostream); + } + + void ServerHello::read_from(std::istream& istream) + { + dml::Record record; + auto *session_id = record.add_field("m_session_id"); + record.add_field("unknown"); + auto *timestamp = record.add_field("m_timestamp"); + auto *milliseconds = record.add_field("m_milliseconds"); + record.read_from(istream); + + m_session_id = session_id->get_value(); + m_timestamp = timestamp->get_value(); + m_milliseconds = milliseconds->get_value(); + } + + size_t ServerHello::get_size() const + { + return sizeof(dml::USHRT) + sizeof(dml::GID) + + sizeof(dml::UINT); + } + + Packet *ServerHello::create_packet(uint16_t session_id, + uint32_t timestamp, uint32_t milliseconds) + { + const ServerHello data(session_id, timestamp, milliseconds); + auto *packet = new Packet(true, (uint8_t)Opcode::SERVER_HELLO); + packet->set_payload_data(data); + return packet; + } } } } \ No newline at end of file From c618e7d53627aa9c1572f8ee4ec7cbc353eed400 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Tue, 3 Apr 2018 21:36:49 +0100 Subject: [PATCH 13/49] protocol: Fix problem with null characters in packet data --- include/ki/protocol/Packet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ki/protocol/Packet.h b/include/ki/protocol/Packet.h index e877c8b..91eb7ab 100644 --- a/include/ki/protocol/Packet.h +++ b/include/ki/protocol/Packet.h @@ -39,7 +39,7 @@ namespace protocol static_assert(std::is_base_of::value, "DataT must derive from Serializable."); - std::istringstream iss(m_payload.data()); + std::istringstream iss(std::string(m_payload.data(), m_payload.size())); DataT *data = new DataT(); data->read_from(iss); return data; From 3e34c0b3bc26336a0cee31f7d4babaee5aa28041 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Thu, 5 Apr 2018 17:36:26 +0100 Subject: [PATCH 14/49] protocol: Implement module loading and I/O for DML messages 'das alota cod --- include/ki/protocol/dml/Message.h | 25 ++- include/ki/protocol/dml/MessageManager.h | 28 +-- include/ki/protocol/dml/MessageModule.h | 14 +- include/ki/protocol/dml/MessageTemplate.h | 2 +- include/ki/protocol/exception.h | 26 +++ src/protocol/dml/Message.cpp | 121 +++++++++++- src/protocol/dml/MessageManager.cpp | 224 ++++++++++++++++++++++ src/protocol/dml/MessageModule.cpp | 128 +++++++++++++ src/protocol/dml/MessageTemplate.cpp | 47 +++++ 9 files changed, 591 insertions(+), 24 deletions(-) create mode 100644 include/ki/protocol/exception.h diff --git a/include/ki/protocol/dml/Message.h b/include/ki/protocol/dml/Message.h index 4709198..096c96c 100644 --- a/include/ki/protocol/dml/Message.h +++ b/include/ki/protocol/dml/Message.h @@ -13,7 +13,7 @@ namespace dml { public: Message(uint8_t service_id = 0, uint8_t type = 0); - virtual ~Message() = default; + virtual ~Message(); uint8_t get_service_id() const; void set_service_id(uint8_t service_id); @@ -21,16 +21,35 @@ namespace dml uint8_t get_type() const; void set_type(uint8_t type); - ki::dml::Record &get_record(); - const ki::dml::Record &get_record() const; + ki::dml::Record *get_record(); + const ki::dml::Record *get_record() const; + + /** + * Sets the record to a copy of the specified record. + */ void set_record(const ki::dml::Record &record); + /** + * If raw data is present, then this uses the specified record + * to parse the raw DML message payload into a new Record. + * If raw data is not present, this is equivalent to set_record. + * + * If the raw data is parsed successfully, the internal raw + * data is cleared, and calls to get_record will return a valid + * Record pointer. + * + * However, if the raw data is not parsed successfully, then + * calls to get_record will still return nullptr. + */ + void use_template_record(const ki::dml::Record &record); + void write_to(std::ostream &ostream) const override final; void read_from(std::istream &istream) override final; size_t get_size() const override final; private: uint8_t m_service_id; uint8_t m_type; + std::vector m_raw_data; ki::dml::Record *m_record; }; } diff --git a/include/ki/protocol/dml/MessageManager.h b/include/ki/protocol/dml/MessageManager.h index be3281e..75e9e16 100644 --- a/include/ki/protocol/dml/MessageManager.h +++ b/include/ki/protocol/dml/MessageManager.h @@ -13,24 +13,28 @@ namespace dml class MessageManager { public: - MessageManager(); + MessageManager() = default; ~MessageManager(); - static MessageManager &get_singleton(); - - const MessageModule &load_module(std::string filepath); - const MessageModule &get_module(uint8_t service_id) const; - const MessageModule &get_module(std::string protocol_type) const; + const MessageModule *load_module(std::string filepath); + const MessageModule *get_module(uint8_t service_id) const; + const MessageModule *get_module(const std::string &protocol_type) const; MessageBuilder &build_message(uint8_t service_id, uint8_t message_type) const; - MessageBuilder &build_message(uint8_t service_id, std::string message_name) const; - MessageBuilder &build_message(std::string service_type, uint8_t message_type) const; - MessageBuilder &build_message(std::string service_type, std::string message_name) const; + MessageBuilder &build_message(uint8_t service_id, const std::string &message_name) const; + MessageBuilder &build_message(const std::string &protocol_type, uint8_t message_type) const; + MessageBuilder &build_message(const std::string &protocol_type, const std::string &message_name) const; - const Message *from_binary(std::istream &istream); + /** + * If the DML message header cannot be read, then a nullptr + * is returned; otherwise, a valid Message pointer is always returned. + * However, that does not mean that the message itself is valid. + * + * To verify if the record was completely parsed, get_record + * should return a valid Record pointer, rather than nullptr. + */ + const Message *message_from_binary(std::istream &istream) const; private: - static MessageManager *g_instance; - MessageModuleList m_modules; MessageModuleServiceIdMap m_service_id_map; MessageModuleProtocolTypeMap m_protocol_type_map; diff --git a/include/ki/protocol/dml/MessageModule.h b/include/ki/protocol/dml/MessageModule.h index 137cc42..94d4e38 100644 --- a/include/ki/protocol/dml/MessageModule.h +++ b/include/ki/protocol/dml/MessageModule.h @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace ki @@ -14,7 +15,7 @@ namespace dml class MessageModule { public: - MessageModule(uint8_t service_id, std::string protocol_type); + MessageModule(uint8_t service_id = 0, std::string protocol_type = ""); ~MessageModule(); uint8_t get_service_id() const; @@ -26,10 +27,9 @@ namespace dml std::string get_protocol_desription() const; void set_protocol_description(std::string protocol_description); - const MessageTemplate &add_message_template(std::string name, uint8_t type, - ki::dml::Record *record); - const MessageTemplate &get_message_template(uint8_t type); - const MessageTemplate &get_message_template(std::string name); + const MessageTemplate *add_message_template(std::string name, ki::dml::Record *record); + const MessageTemplate *get_message_template(uint8_t type) const; + const MessageTemplate *get_message_template(std::string name) const; MessageBuilder &build_message(uint8_t message_type) const; MessageBuilder &build_message(std::string message_name) const; @@ -37,10 +37,10 @@ namespace dml uint8_t m_service_id; std::string m_protocol_type; std::string m_protocol_description; + uint8_t m_last_message_type; - std::vector m_templates; + std::array m_templates; std::map m_message_name_map; - std::map m_message_type_map; }; typedef std::vector MessageModuleList; diff --git a/include/ki/protocol/dml/MessageTemplate.h b/include/ki/protocol/dml/MessageTemplate.h index ba8b744..178c152 100644 --- a/include/ki/protocol/dml/MessageTemplate.h +++ b/include/ki/protocol/dml/MessageTemplate.h @@ -24,7 +24,7 @@ namespace dml const ki::dml::Record &get_record() const; void set_record(ki::dml::Record *record); - MessageBuilder &build_message(); + MessageBuilder &build_message() const; private: std::string m_name; uint8_t m_type; diff --git a/include/ki/protocol/exception.h b/include/ki/protocol/exception.h new file mode 100644 index 0000000..998c011 --- /dev/null +++ b/include/ki/protocol/exception.h @@ -0,0 +1,26 @@ +#pragma once +#include + +namespace ki +{ +namespace protocol +{ + class runtime_error : public std::runtime_error + { + public: + runtime_error(std::string message) : std::runtime_error(message) { } + }; + + class parse_error : public runtime_error + { + public: + parse_error(std::string message) : runtime_error(message) { } + }; + + class value_error : public runtime_error + { + public: + value_error(std::string message) : runtime_error(message) { } + }; +} +} \ No newline at end of file diff --git a/src/protocol/dml/Message.cpp b/src/protocol/dml/Message.cpp index d9a4dab..8fdef6f 100644 --- a/src/protocol/dml/Message.cpp +++ b/src/protocol/dml/Message.cpp @@ -1,4 +1,5 @@ #include "ki/protocol/dml/Message.h" +#include "ki/protocol/exception.h" namespace ki { @@ -6,7 +7,125 @@ namespace protocol { namespace dml { - + Message::Message(uint8_t service_id, uint8_t type) + { + m_service_id = service_id; + m_type = type; + } + + Message::~Message() + { + delete m_record; + } + + uint8_t Message::get_service_id() const + { + return m_service_id; + } + + void Message::set_service_id(uint8_t service_id) + { + m_service_id = service_id; + } + + uint8_t Message::get_type() const + { + return m_type; + } + + void Message::set_type(uint8_t type) + { + m_type = type; + } + + ki::dml::Record *Message::get_record() + { + return m_record; + } + + const ki::dml::Record *Message::get_record() const + { + return m_record; + } + + void Message::set_record(const ki::dml::Record &record) + { + m_record = new ki::dml::Record(record); + } + + void Message::use_template_record(const ki::dml::Record &record) + { + set_record(record); + if (!m_raw_data.empty()) + { + std::istringstream iss(std::string(m_raw_data.data(), m_raw_data.size())); + try + { + m_record->read_from(iss); + m_raw_data.clear(); + } + catch (ki::dml::parse_error &e) + { + delete m_record; + m_record = nullptr; + + std::ostringstream oss; + oss << "Error reading DML message payload: " << e.what(); + throw parse_error(oss.str()); + } + } + } + + void Message::write_to(std::ostream &ostream) const + { + ki::dml::Record record; + record.add_field("m_service_id")->set_value(m_service_id); + record.add_field("m_type")->set_value(m_type); + auto *size_field = record.add_field("size"); + if (m_record) + size_field->set_value(m_record->get_size() + 4); + else + size_field->set_value(m_raw_data.size() + 4); + record.write_to(ostream); + + if (m_record) + record.write_to(ostream); + else + ostream.write(m_raw_data.data(), m_raw_data.size()); + } + + void Message::read_from(std::istream &istream) + { + ki::dml::Record record; + auto *service_id_field = record.add_field("ServiceID"); + auto *message_type_field = record.add_field("MsgType"); + auto *size_field = record.add_field("Length"); + try + { + record.read_from(istream); + } + catch (ki::dml::parse_error &e) + { + std::ostringstream oss; + oss << "Error reading DML message header: " << e.what(); + throw parse_error(oss.str()); + } + + m_service_id = service_id_field->get_value(); + m_type = message_type_field->get_value(); + const ki::dml::USHRT size = size_field->get_value() - 4; + m_raw_data.resize(size); + istream.read(m_raw_data.data(), size); + if (istream.fail()) + throw parse_error("Not enough data was available to read DML message payload."); + } + + size_t Message::get_size() const + { + if (m_record) + return 4 + m_record->get_size(); + return 4 + m_raw_data.size(); + } } } } \ No newline at end of file diff --git a/src/protocol/dml/MessageManager.cpp b/src/protocol/dml/MessageManager.cpp index ae3703d..73eee25 100644 --- a/src/protocol/dml/MessageManager.cpp +++ b/src/protocol/dml/MessageManager.cpp @@ -1,4 +1,10 @@ #include "ki/protocol/dml/MessageManager.h" +#include "ki/protocol/exception.h" +#include "ki/dml/Record.h" +#include "ki/util/ValueBytes.h" +#include +#include +#include namespace ki { @@ -6,7 +12,225 @@ namespace protocol { namespace dml { + MessageManager::~MessageManager() + { + for (auto it = m_modules.begin(); + it != m_modules.end(); ++it) + delete *it; + m_modules.clear(); + m_service_id_map.clear(); + m_protocol_type_map.clear(); + } + const MessageModule *MessageManager::load_module(std::string filepath) + { + // Open the file + std::ifstream ifs(filepath, std::ios::ate); + if (!ifs.is_open()) + { + std::ostringstream oss; + oss << "Could not open file: " << filepath; + throw value_error(oss.str()); + } + + // Load contents into memory + size_t size = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + char *data = new char[size + 1] { 0 }; + ifs.read(data, size); + + // Parse the contents + rapidxml::xml_document<> doc; + try + { + doc.parse<0>(data); + } + catch (rapidxml::parse_error &e) + { + delete[] data; + + std::ostringstream oss; + oss << "Failed to parse: " << filepath; + throw parse_error(oss.str()); + } + + // It's safe to allocate the module we're working on now + auto *message_module = new MessageModule(); + + // Get the root node and iterate through children + // Each child is a MessageTemplate + auto *root = doc.first_node(); + for (auto *node = root->first_node(); + node; node = node->next_sibling()) + { + // Parse the record node inside this node + auto *record_node = node->first_node(); + if (!record_node) + continue; + auto *record = new ki::dml::Record(); + record->from_xml(record_node); + + // The message name is initially based on the element name + const std::string message_name = node->name(); + if (message_name == "_ProtocolInfo") + { + auto *service_id_field = record->get_field("ServiceID"); + auto *type_field = record->get_field("ProtocolType"); + auto *description_field = record->get_field("ProtocolDescription"); + + // Set the module metadata from this template + if (service_id_field) + message_module->set_service_id(service_id_field->get_value()); + if (type_field) + message_module->set_protocol_type(type_field->get_value()); + if (description_field) + message_module->set_protocol_description(description_field->get_value()); + } + else + { + // The template will use the record itself to figure out name and type; + // we only give the XML data incase the record doesn't have it defined. + auto *message_template = message_module->add_message_template(message_name, record); + if (!message_template) + { + delete[] data; + delete message_module; + delete record; + + std::ostringstream oss; + oss << "Failed to create message template for "; + oss << message_name; + throw value_error(oss.str()); + } + } + } + + // Make sure we aren't overwriting another module + if (m_service_id_map.count(message_module->get_service_id()) == 1) + { + delete[] data; + delete message_module; + + std::ostringstream oss; + oss << "Message Module has already been loaded with Service ID "; + oss << message_module->get_service_id(); + throw value_error(oss.str()); + } + + if (m_protocol_type_map.count(message_module->get_protocol_type()) == 1) + { + delete[] data; + delete message_module; + + std::ostringstream oss; + oss << "Message Module has already been loaded with Protocol Type "; + oss << message_module->get_protocol_type(); + throw value_error(oss.str()); + } + + // Add it to our maps + m_modules.push_back(message_module); + m_service_id_map.insert({ message_module->get_service_id(), message_module }); + m_protocol_type_map.insert({ message_module->get_protocol_type(), message_module }); + + delete[] data; + return message_module; + } + + const MessageModule *MessageManager::get_module(uint8_t service_id) const + { + if (m_service_id_map.count(service_id) == 1) + return m_service_id_map.at(service_id); + return nullptr; + } + + const MessageModule *MessageManager::get_module(const std::string &protocol_type) const + { + if (m_protocol_type_map.count(protocol_type) == 1) + return m_protocol_type_map.at(protocol_type); + return nullptr; + } + + MessageBuilder &MessageManager::build_message(uint8_t service_id, uint8_t message_type) const + { + auto *message_module = get_module(service_id); + if (!message_module) + { + std::ostringstream oss; + oss << "No service exists with id: " << service_id; + throw value_error(oss.str()); + } + + return message_module->build_message(message_type); + } + + MessageBuilder& MessageManager::build_message(uint8_t service_id, const std::string& message_name) const + { + auto *message_module = get_module(service_id); + if (!message_module) + { + std::ostringstream oss; + oss << "No service exists with id: " << service_id; + throw value_error(oss.str()); + } + + return message_module->build_message(message_name); + } + + MessageBuilder& MessageManager::build_message(const std::string& protocol_type, uint8_t message_type) const + { + auto *message_module = get_module(protocol_type); + if (!message_module) + { + std::ostringstream oss; + oss << "No service exists with protocol type: " << protocol_type; + throw value_error(oss.str()); + } + + return message_module->build_message(message_type); + } + + MessageBuilder& MessageManager::build_message(const std::string& protocol_type, const std::string& message_name) const + { + auto *message_module = get_module(protocol_type); + if (!message_module) + { + std::ostringstream oss; + oss << "No service exists with protocol type: " << protocol_type; + throw value_error(oss.str()); + } + + return message_module->build_message(message_name); + } + + const Message *MessageManager::message_from_binary(std::istream& istream) const + { + // Read the message header and raw payload + Message *message = new Message(); + try + { + message->read_from(istream); + } + catch (parse_error &e) + { + delete message; + return nullptr; + } + + // Get the message module that uses the specified service id + auto *message_module = get_module(message->get_service_id()); + if (!message_module) + return message; + + // Get the message template for this message type + auto *message_template = message_module->get_message_template(message->get_type()); + if (!message_template) + return message; + + // Parse the raw payload with the template + message->use_template_record(message_template->get_record()); + return message; + } } } } \ No newline at end of file diff --git a/src/protocol/dml/MessageModule.cpp b/src/protocol/dml/MessageModule.cpp index 9b239fc..4d2c626 100644 --- a/src/protocol/dml/MessageModule.cpp +++ b/src/protocol/dml/MessageModule.cpp @@ -1,4 +1,6 @@ #include "ki/protocol/dml/MessageModule.h" +#include "ki/protocol/exception.h" +#include namespace ki { @@ -6,7 +8,133 @@ namespace protocol { namespace dml { + MessageModule::MessageModule(uint8_t service_id, std::string protocol_type) + { + m_service_id = service_id; + m_protocol_type = protocol_type; + m_protocol_description = ""; + m_last_message_type = 0; + m_templates = std::array { nullptr }; + } + MessageModule::~MessageModule() + { + for (auto it = m_templates.begin(); + it != m_templates.end(); ++it) + delete *it; + m_message_name_map.clear(); + } + + uint8_t MessageModule::get_service_id() const + { + return m_service_id; + } + + void MessageModule::set_service_id(uint8_t service_id) + { + m_service_id = service_id; + } + + std::string MessageModule::get_protocol_type() const + { + return m_protocol_type; + } + + void MessageModule::set_protocol_type(std::string protocol_type) + { + m_protocol_type = protocol_type; + } + + std::string MessageModule::get_protocol_desription() const + { + return m_protocol_description; + } + + void MessageModule::set_protocol_description(std::string protocol_description) + { + m_protocol_description = protocol_description; + } + + const MessageTemplate *MessageModule::add_message_template(std::string name, ki::dml::Record *record) + { + if (!record) + return nullptr; + + // If the field exists, get the name from the record rather than the XML + auto *name_field = record->get_field("_MsgName"); + if (name_field) + name = name_field->get_value(); + + // Do we already have a message template with this name? + if (m_message_name_map.count(name) == 1) + return nullptr; + + // Message type is based on the _MsgOrder field if it's present + // Otherwise it just goes in order of added templates + uint8_t message_type; + auto *order_field = record->get_field("_MsgOrder"); + if (order_field) + message_type = order_field->get_value(); + else + message_type = m_last_message_type + 1; + + // Don't allow message type to be zero + if (message_type == 0) + return nullptr; + + // Do we already have a message template with this type? + if (m_templates[message_type] != nullptr) + return nullptr; + + // Create the template and add it to our maps + auto *message_template = new MessageTemplate(name, message_type, record); + m_templates[message_type] = message_template; + m_message_name_map.insert({ name, message_template }); + m_last_message_type = message_type; + return message_template; + } + + const MessageTemplate *MessageModule::get_message_template(uint8_t type) const + { + return m_templates[type]; + } + + const MessageTemplate *MessageModule::get_message_template(std::string name) const + { + if (m_message_name_map.count(name) == 1) + return m_message_name_map.at(name); + return nullptr; + } + + MessageBuilder& MessageModule::build_message(uint8_t message_type) const + { + auto *message_template = get_message_template(message_type); + if (!message_template) + { + std::ostringstream oss; + oss << "No message exists with type: " << message_type; + oss << "(service=" << m_protocol_type << ")"; + throw value_error(oss.str()); + } + + return message_template->build_message() + .set_service_id(m_service_id); + } + + MessageBuilder &MessageModule::build_message(std::string message_name) const + { + auto *message_template = get_message_template(message_name); + if (!message_template) + { + std::ostringstream oss; + oss << "No message exists with name: " << message_name; + oss << "(service=" << m_protocol_type << ")"; + throw value_error(oss.str()); + } + + return message_template->build_message() + .set_service_id(m_service_id); + } } } } \ No newline at end of file diff --git a/src/protocol/dml/MessageTemplate.cpp b/src/protocol/dml/MessageTemplate.cpp index 405de3f..fdb3b26 100644 --- a/src/protocol/dml/MessageTemplate.cpp +++ b/src/protocol/dml/MessageTemplate.cpp @@ -6,7 +6,54 @@ namespace protocol { namespace dml { + MessageTemplate::MessageTemplate(std::string name, uint8_t type, ki::dml::Record* record) + { + m_name = name; + m_type = type; + m_record = record; + } + MessageTemplate::~MessageTemplate() + { + delete m_record; + } + + std::string MessageTemplate::get_name() const + { + return m_name; + } + + void MessageTemplate::set_name(std::string name) + { + m_name = name; + } + + uint8_t MessageTemplate::get_type() const + { + return m_type; + } + + void MessageTemplate::set_type(uint8_t type) + { + m_type = type; + } + + const ki::dml::Record& MessageTemplate::get_record() const + { + return *m_record; + } + + void MessageTemplate::set_record(ki::dml::Record* record) + { + m_record = record; + } + + MessageBuilder &MessageTemplate::build_message() const + { + return MessageBuilder() + .set_message_type(m_type) + .use_template_record(*m_record); + } } } } \ No newline at end of file From 6a84d50cee3e7bb40937dab6d070609f9be9cd15 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Thu, 5 Apr 2018 18:09:56 +0100 Subject: [PATCH 15/49] examples: Example for loading DML message modules --- examples/src/example-dml-module.cpp | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 examples/src/example-dml-module.cpp diff --git a/examples/src/example-dml-module.cpp b/examples/src/example-dml-module.cpp new file mode 100644 index 0000000..108df4f --- /dev/null +++ b/examples/src/example-dml-module.cpp @@ -0,0 +1,64 @@ +#include +#include +#include + +using namespace ki::protocol; + +int main(int argc, char **argv) +{ + // Get command-line arguments + if (argc < 3) + { + std::cout << "usage: example-dml-module.exe " << std::endl; + std::cout << "Prints out information for specified message." << std::endl; + return 1; + } + + // Create a manager to load modules into + auto *message_manager = new dml::MessageManager(); + const dml::MessageModule *message_module; + + // Load the message module file + const std::string filepath = argv[1]; + try + { + message_module = message_manager->load_module(filepath); + } + catch (value_error &e) + { + std::cout << "Failed to load message module."; + return 1; + } + + // Print some information about the module itself + std::cout << "Service ID: " << (uint16_t)message_module->get_service_id() << std::endl; + std::cout << "Protocol Type: " << message_module->get_protocol_type() << std::endl; + + // Get the message template from the module we just loaded + const std::string message_name = argv[2]; + auto *message_template = message_module->get_message_template(message_name); + if (message_template) + { + std::cout << "Message Name: " << message_template->get_name() << std::endl; + std::cout << "Mesasge Type: " << (uint16_t)message_template->get_type() << std::endl; + + // Print out the fields in the template record + std::cout << std::endl; + auto &record = message_template->get_record(); + for (auto it = record.fields_begin(); + it != record.fields_end(); ++it) + { + auto *field = *it; + if (field->is_transferable()) + std::cout << field->get_type_name() << " " << field->get_name() << ";" << std::endl; + } + } + else + { + std::cout << "Could not find message with name: " << message_name << std::endl; + return 1; + } + + // Exit successfully + return 0; +} From 9ef780ace1a076a6fb6a3db8bb560584d8601627 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 6 Apr 2018 03:07:35 +0100 Subject: [PATCH 16/49] protocol: Fix message types in modules that don't specify _MsgOrder --- include/ki/protocol/dml/MessageModule.h | 6 +++-- src/protocol/dml/MessageManager.cpp | 13 ++++++++++ src/protocol/dml/MessageModule.cpp | 32 +++++++++++++++++++++---- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/include/ki/protocol/dml/MessageModule.h b/include/ki/protocol/dml/MessageModule.h index 94d4e38..b92c3b1 100644 --- a/include/ki/protocol/dml/MessageModule.h +++ b/include/ki/protocol/dml/MessageModule.h @@ -3,7 +3,6 @@ #include #include #include -#include #include namespace ki @@ -31,6 +30,8 @@ namespace dml const MessageTemplate *get_message_template(uint8_t type) const; const MessageTemplate *get_message_template(std::string name) const; + void sort_lookup(); + MessageBuilder &build_message(uint8_t message_type) const; MessageBuilder &build_message(std::string message_name) const; private: @@ -39,7 +40,8 @@ namespace dml std::string m_protocol_description; uint8_t m_last_message_type; - std::array m_templates; + std::vector m_templates; + std::map m_message_type_map; std::map m_message_name_map; }; diff --git a/src/protocol/dml/MessageManager.cpp b/src/protocol/dml/MessageManager.cpp index 73eee25..3afc052 100644 --- a/src/protocol/dml/MessageManager.cpp +++ b/src/protocol/dml/MessageManager.cpp @@ -56,6 +56,8 @@ namespace dml // It's safe to allocate the module we're working on now auto *message_module = new MessageModule(); + bool sort_required = true; + bool first_message = true; // Get the root node and iterate through children // Each child is a MessageTemplate @@ -88,6 +90,13 @@ namespace dml } else { + // Base sorting on whether the first message has the _MsgOrder field. + if (first_message) + { + sort_required = !record->has_field("_MsgOrder"); + first_message = false; + } + // The template will use the record itself to figure out name and type; // we only give the XML data incase the record doesn't have it defined. auto *message_template = message_module->add_message_template(message_name, record); @@ -105,6 +114,10 @@ namespace dml } } + // Sort the module's lookup if we need to + if (sort_required) + message_module->sort_lookup(); + // Make sure we aren't overwriting another module if (m_service_id_map.count(message_module->get_service_id()) == 1) { diff --git a/src/protocol/dml/MessageModule.cpp b/src/protocol/dml/MessageModule.cpp index 4d2c626..c02739d 100644 --- a/src/protocol/dml/MessageModule.cpp +++ b/src/protocol/dml/MessageModule.cpp @@ -14,7 +14,6 @@ namespace dml m_protocol_type = protocol_type; m_protocol_description = ""; m_last_message_type = 0; - m_templates = std::array { nullptr }; } MessageModule::~MessageModule() @@ -67,7 +66,7 @@ namespace dml // Do we already have a message template with this name? if (m_message_name_map.count(name) == 1) - return nullptr; + return m_message_name_map.at(name); // Message type is based on the _MsgOrder field if it's present // Otherwise it just goes in order of added templates @@ -83,12 +82,13 @@ namespace dml return nullptr; // Do we already have a message template with this type? - if (m_templates[message_type] != nullptr) + if (m_message_type_map.count(message_type) == 1) return nullptr; // Create the template and add it to our maps auto *message_template = new MessageTemplate(name, message_type, record); - m_templates[message_type] = message_template; + m_templates.push_back(message_template); + m_message_type_map.insert({ message_type, message_template }); m_message_name_map.insert({ name, message_template }); m_last_message_type = message_type; return message_template; @@ -96,7 +96,9 @@ namespace dml const MessageTemplate *MessageModule::get_message_template(uint8_t type) const { - return m_templates[type]; + if (m_message_type_map.count(type) == 1) + return m_message_type_map.at(type); + return nullptr; } const MessageTemplate *MessageModule::get_message_template(std::string name) const @@ -106,6 +108,26 @@ namespace dml return nullptr; } + void MessageModule::sort_lookup() + { + uint8_t message_type = 1; + + // First, clear the message type map since we're going to be + // moving everything around + m_message_type_map.clear(); + + // Iterating over a map with std::string as the key + // is guaranteed to be in alphabetical order + for (auto it = m_message_name_map.begin(); + it != m_message_name_map.end(); ++it) + { + auto *message_template = it->second; + message_template->set_type(message_type); + m_message_type_map.insert({ message_type, message_template }); + message_type++; + } + } + MessageBuilder& MessageModule::build_message(uint8_t message_type) const { auto *message_template = get_message_template(message_type); From b5909878f81483af63e261263343b26256c9e727 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 6 Apr 2018 04:14:37 +0100 Subject: [PATCH 17/49] protocol: Use an auto sorting system for message types Rather than calling sort_lookup externally --- include/ki/protocol/dml/MessageModule.h | 3 +- include/ki/protocol/dml/MessageTemplate.h | 7 +++- src/protocol/dml/MessageManager.cpp | 17 ++------ src/protocol/dml/MessageModule.cpp | 47 ++++++++++++++--------- src/protocol/dml/MessageTemplate.cpp | 15 +++++++- 5 files changed, 54 insertions(+), 35 deletions(-) diff --git a/include/ki/protocol/dml/MessageModule.h b/include/ki/protocol/dml/MessageModule.h index b92c3b1..3b763c3 100644 --- a/include/ki/protocol/dml/MessageModule.h +++ b/include/ki/protocol/dml/MessageModule.h @@ -26,7 +26,8 @@ namespace dml std::string get_protocol_desription() const; void set_protocol_description(std::string protocol_description); - const MessageTemplate *add_message_template(std::string name, ki::dml::Record *record); + const MessageTemplate *add_message_template(std::string name, + ki::dml::Record *record, bool auto_sort = true); const MessageTemplate *get_message_template(uint8_t type) const; const MessageTemplate *get_message_template(std::string name) const; diff --git a/include/ki/protocol/dml/MessageTemplate.h b/include/ki/protocol/dml/MessageTemplate.h index 178c152..5cf3375 100644 --- a/include/ki/protocol/dml/MessageTemplate.h +++ b/include/ki/protocol/dml/MessageTemplate.h @@ -12,7 +12,8 @@ namespace dml class MessageTemplate { public: - MessageTemplate(std::string name, uint8_t type, ki::dml::Record *record); + MessageTemplate(std::string name, uint8_t type, + uint8_t service_id, ki::dml::Record *record); ~MessageTemplate(); std::string get_name() const; @@ -21,6 +22,9 @@ namespace dml uint8_t get_type() const; void set_type(uint8_t type); + uint8_t get_service_id() const; + void set_service_id(uint8_t service_id); + const ki::dml::Record &get_record() const; void set_record(ki::dml::Record *record); @@ -28,6 +32,7 @@ namespace dml private: std::string m_name; uint8_t m_type; + uint8_t m_service_id; ki::dml::Record *m_record; }; } diff --git a/src/protocol/dml/MessageManager.cpp b/src/protocol/dml/MessageManager.cpp index 3afc052..90903ae 100644 --- a/src/protocol/dml/MessageManager.cpp +++ b/src/protocol/dml/MessageManager.cpp @@ -56,8 +56,6 @@ namespace dml // It's safe to allocate the module we're working on now auto *message_module = new MessageModule(); - bool sort_required = true; - bool first_message = true; // Get the root node and iterate through children // Each child is a MessageTemplate @@ -90,16 +88,13 @@ namespace dml } else { - // Base sorting on whether the first message has the _MsgOrder field. - if (first_message) - { - sort_required = !record->has_field("_MsgOrder"); - first_message = false; - } + // Only do sorting after we've reached the final message + // This only affects modules that aren't ordered with _MsgOrder. + const bool auto_sort = node->next_sibling() == nullptr; // The template will use the record itself to figure out name and type; // we only give the XML data incase the record doesn't have it defined. - auto *message_template = message_module->add_message_template(message_name, record); + auto *message_template = message_module->add_message_template(message_name, record, auto_sort); if (!message_template) { delete[] data; @@ -114,10 +109,6 @@ namespace dml } } - // Sort the module's lookup if we need to - if (sort_required) - message_module->sort_lookup(); - // Make sure we aren't overwriting another module if (m_service_id_map.count(message_module->get_service_id()) == 1) { diff --git a/src/protocol/dml/MessageModule.cpp b/src/protocol/dml/MessageModule.cpp index c02739d..6345507 100644 --- a/src/protocol/dml/MessageModule.cpp +++ b/src/protocol/dml/MessageModule.cpp @@ -21,6 +21,7 @@ namespace dml for (auto it = m_templates.begin(); it != m_templates.end(); ++it) delete *it; + m_message_type_map.clear(); m_message_name_map.clear(); } @@ -54,7 +55,8 @@ namespace dml m_protocol_description = protocol_description; } - const MessageTemplate *MessageModule::add_message_template(std::string name, ki::dml::Record *record) + const MessageTemplate *MessageModule::add_message_template(std::string name, + ki::dml::Record *record, bool auto_sort) { if (!record) return nullptr; @@ -69,28 +71,33 @@ namespace dml return m_message_name_map.at(name); // Message type is based on the _MsgOrder field if it's present - // Otherwise it just goes in order of added templates - uint8_t message_type; + // Otherwise it's based on the alphabetical order of template names + uint8_t message_type = 0; auto *order_field = record->get_field("_MsgOrder"); if (order_field) + { message_type = order_field->get_value(); - else - message_type = m_last_message_type + 1; - // Don't allow message type to be zero - if (message_type == 0) - return nullptr; + // Don't allow message type to be 0 + if (message_type == 0) + return nullptr; - // Do we already have a message template with this type? - if (m_message_type_map.count(message_type) == 1) - return nullptr; + // Do we already have a template with this type? + if (m_message_type_map.count(message_type) == 1) + return nullptr; + } - // Create the template and add it to our maps - auto *message_template = new MessageTemplate(name, message_type, record); + // Create the message template and add it to our lookups + auto *message_template = new MessageTemplate(name, message_type, m_service_id, record); m_templates.push_back(message_template); - m_message_type_map.insert({ message_type, message_template }); m_message_name_map.insert({ name, message_template }); - m_last_message_type = message_type; + + // Is this module ordered? + if (message_type != 0) + m_message_type_map.insert({ message_type, message_template }); + else if (auto_sort) + sort_lookup(); + return message_template; } @@ -125,6 +132,10 @@ namespace dml message_template->set_type(message_type); m_message_type_map.insert({ message_type, message_template }); message_type++; + + // Make sure we haven't overflowed + if (message_type == 0) + throw value_error("Module has more than 254 messages."); } } @@ -139,8 +150,7 @@ namespace dml throw value_error(oss.str()); } - return message_template->build_message() - .set_service_id(m_service_id); + return message_template->build_message(); } MessageBuilder &MessageModule::build_message(std::string message_name) const @@ -154,8 +164,7 @@ namespace dml throw value_error(oss.str()); } - return message_template->build_message() - .set_service_id(m_service_id); + return message_template->build_message(); } } } diff --git a/src/protocol/dml/MessageTemplate.cpp b/src/protocol/dml/MessageTemplate.cpp index fdb3b26..e89db88 100644 --- a/src/protocol/dml/MessageTemplate.cpp +++ b/src/protocol/dml/MessageTemplate.cpp @@ -6,10 +6,12 @@ namespace protocol { namespace dml { - MessageTemplate::MessageTemplate(std::string name, uint8_t type, ki::dml::Record* record) + MessageTemplate::MessageTemplate(std::string name, uint8_t type, + uint8_t service_id, ki::dml::Record* record) { m_name = name; m_type = type; + m_service_id = service_id; m_record = record; } @@ -38,6 +40,16 @@ namespace dml m_type = type; } + uint8_t MessageTemplate::get_service_id() const + { + return m_service_id; + } + + void MessageTemplate::set_service_id(uint8_t service_id) + { + m_service_id = service_id; + } + const ki::dml::Record& MessageTemplate::get_record() const { return *m_record; @@ -52,6 +64,7 @@ namespace dml { return MessageBuilder() .set_message_type(m_type) + .set_service_id(m_service_id) .use_template_record(*m_record); } } From f2247f03f48257651e67c243b72445149a4a1d43 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 6 Apr 2018 04:24:10 +0100 Subject: [PATCH 18/49] protocol: Fix typo in MessageBuilder --- include/ki/protocol/dml/MessageBuilder.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/include/ki/protocol/dml/MessageBuilder.h b/include/ki/protocol/dml/MessageBuilder.h index 2b2e134..33995eb 100644 --- a/include/ki/protocol/dml/MessageBuilder.h +++ b/include/ki/protocol/dml/MessageBuilder.h @@ -1,6 +1,8 @@ #pragma once #include "Message.h" +#include "ki/protocol/exception.h" #include +#include namespace ki { @@ -20,10 +22,12 @@ namespace dml template MessageBuilder &set_field_value(std::string name, ValueT value) { - auto *field = m_message->get_record().get_field(name); + auto *field = m_message->get_record()->get_field(name); if (!field) { - // TODO: Exceptions + std::ostringstream oss; + oss << "No field with name " << name << " exists with specified type."; + throw value_error(oss.str()); } field->set_value(value); return *this; @@ -35,4 +39,4 @@ namespace dml }; } } -} \ No newline at end of file +} From a13731ccc02f4a7312a739302c2692a0c5b05344 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 6 Apr 2018 04:52:58 +0100 Subject: [PATCH 19/49] protocol: Fix memory leak in Packet when data isn't read succesfully --- include/ki/protocol/Packet.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/ki/protocol/Packet.h b/include/ki/protocol/Packet.h index 91eb7ab..ef56c05 100644 --- a/include/ki/protocol/Packet.h +++ b/include/ki/protocol/Packet.h @@ -1,5 +1,6 @@ #pragma once #include "../util/Serializable.h" +#include "exception.h" #include #include #include @@ -41,7 +42,15 @@ namespace protocol std::istringstream iss(std::string(m_payload.data(), m_payload.size())); DataT *data = new DataT(); - data->read_from(iss); + try + { + data->read_from(iss); + } + catch (parse_error &e) + { + delete data; + throw; + } return data; } From 2f51a53cb370d63af8718b27650f88be6ff1957d Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 6 Apr 2018 05:36:54 +0100 Subject: [PATCH 20/49] protocol: Message record should start off as a nullptr --- src/protocol/dml/Message.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/protocol/dml/Message.cpp b/src/protocol/dml/Message.cpp index 8fdef6f..107dc56 100644 --- a/src/protocol/dml/Message.cpp +++ b/src/protocol/dml/Message.cpp @@ -11,6 +11,7 @@ namespace dml { m_service_id = service_id; m_type = type; + m_record = nullptr; } Message::~Message() From 611943e3adf2176b7d7b8f96a88e59d2321e4d0b Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 9 Apr 2018 21:49:11 +0100 Subject: [PATCH 21/49] protocol: Remove the Packet class It doesn't need to exist --- include/ki/protocol/control/ClientHello.h | 13 ++++------ include/ki/protocol/control/Ping.h | 6 ----- include/ki/protocol/control/ServerHello.h | 13 ++++------ src/protocol/control/ClientHello.cpp | 27 +++++++-------------- src/protocol/control/Ping.cpp | 10 -------- src/protocol/control/ServerHello.cpp | 29 ++++++++--------------- 6 files changed, 27 insertions(+), 71 deletions(-) diff --git a/include/ki/protocol/control/ClientHello.h b/include/ki/protocol/control/ClientHello.h index f126d8c..98900a6 100644 --- a/include/ki/protocol/control/ClientHello.h +++ b/include/ki/protocol/control/ClientHello.h @@ -1,7 +1,5 @@ #pragma once #include "../../util/Serializable.h" -#include "../Packet.h" -#include "Opcode.h" #include #include @@ -15,14 +13,14 @@ namespace control { public: ClientHello(uint16_t session_id = 0, - uint32_t timestamp = 0, uint32_t milliseconds = 0); + int32_t timestamp = 0, uint32_t milliseconds = 0); virtual ~ClientHello() = default; uint16_t get_session_id() const; void set_session_id(uint16_t session_id); - uint32_t get_timestamp() const; - void set_timestamp(uint32_t timestamp); + int32_t get_timestamp() const; + void set_timestamp(int32_t timestamp); uint32_t get_milliseconds() const; void set_milliseconds(uint32_t milliseconds); @@ -30,12 +28,9 @@ namespace control void write_to(std::ostream &ostream) const override final; void read_from(std::istream &istream) override final; size_t get_size() const override final; - - static Packet *create_packet(uint16_t session_id = 0, - uint32_t timestamp = 0, uint32_t milliseconds = 0); private: uint16_t m_session_id; - uint32_t m_timestamp; + int32_t m_timestamp; uint32_t m_milliseconds; }; } diff --git a/include/ki/protocol/control/Ping.h b/include/ki/protocol/control/Ping.h index 5ab0b33..1b102c0 100644 --- a/include/ki/protocol/control/Ping.h +++ b/include/ki/protocol/control/Ping.h @@ -1,7 +1,5 @@ #pragma once #include "../../util/Serializable.h" -#include "../Packet.h" -#include "Opcode.h" #include namespace ki @@ -29,10 +27,6 @@ namespace control void write_to(std::ostream &ostream) const override final; void read_from(std::istream &istream) override final; size_t get_size() const override final; - - static Packet *create_packet(uint16_t session_id = 0, - uint16_t milliseconds = 0, uint8_t minutes = 0, - bool response = false); private: uint16_t m_session_id; uint16_t m_milliseconds; diff --git a/include/ki/protocol/control/ServerHello.h b/include/ki/protocol/control/ServerHello.h index f94106b..dd76843 100644 --- a/include/ki/protocol/control/ServerHello.h +++ b/include/ki/protocol/control/ServerHello.h @@ -1,7 +1,5 @@ #pragma once #include "../../util/Serializable.h" -#include "../Packet.h" -#include "Opcode.h" #include #include @@ -15,14 +13,14 @@ namespace control { public: ServerHello(uint16_t session_id = 0, - uint32_t timestamp = 0, uint32_t milliseconds = 0); + int32_t timestamp = 0, uint32_t milliseconds = 0); virtual ~ServerHello() = default; uint16_t get_session_id() const; void set_session_id(uint16_t session_id); - uint32_t get_timestamp() const; - void set_timestamp(uint32_t timestamp); + int32_t get_timestamp() const; + void set_timestamp(int32_t timestamp); uint32_t get_milliseconds() const; void set_milliseconds(uint32_t milliseconds); @@ -30,12 +28,9 @@ namespace control void write_to(std::ostream &ostream) const override final; void read_from(std::istream &istream) override final; size_t get_size() const override final; - - static Packet *create_packet(uint16_t session_id = 0, - uint32_t timestamp = 0, uint32_t milliseconds = 0); private: uint16_t m_session_id; - uint32_t m_timestamp; + int32_t m_timestamp; uint32_t m_milliseconds; }; } diff --git a/src/protocol/control/ClientHello.cpp b/src/protocol/control/ClientHello.cpp index 50b7cd9..6d6e819 100644 --- a/src/protocol/control/ClientHello.cpp +++ b/src/protocol/control/ClientHello.cpp @@ -7,8 +7,8 @@ namespace protocol { namespace control { - ClientHello::ClientHello(uint16_t session_id, - uint32_t timestamp, uint32_t milliseconds) + ClientHello::ClientHello(const uint16_t session_id, + const int32_t timestamp, const uint32_t milliseconds) { m_session_id = session_id; m_timestamp = timestamp; @@ -20,17 +20,17 @@ namespace control return m_session_id; } - void ClientHello::set_session_id(uint16_t session_id) + void ClientHello::set_session_id(const uint16_t session_id) { m_session_id = session_id; } - uint32_t ClientHello::get_timestamp() const + int32_t ClientHello::get_timestamp() const { return m_timestamp; } - void ClientHello::set_timestamp(uint32_t timestamp) + void ClientHello::set_timestamp(const int32_t timestamp) { m_timestamp = timestamp; } @@ -40,7 +40,7 @@ namespace control return m_milliseconds; } - void ClientHello::set_milliseconds(uint32_t milliseconds) + void ClientHello::set_milliseconds(const uint32_t milliseconds) { m_milliseconds = milliseconds; } @@ -50,7 +50,7 @@ namespace control dml::Record record; record.add_field("unknown"); record.add_field("unknown2"); - record.add_field("m_timestamp")->set_value(m_timestamp); + record.add_field("m_timestamp")->set_value(m_timestamp); record.add_field("m_milliseconds")->set_value(m_milliseconds); record.add_field("m_session_id")->set_value(m_session_id); record.write_to(ostream); @@ -61,7 +61,7 @@ namespace control dml::Record record; record.add_field("unknown"); record.add_field("unknown2"); - auto *timestamp = record.add_field("m_timestamp"); + auto *timestamp = record.add_field("m_timestamp"); auto *milliseconds = record.add_field("m_milliseconds"); auto *session_id = record.add_field("m_session_id"); record.read_from(istream); @@ -74,18 +74,9 @@ namespace control size_t ClientHello::get_size() const { return sizeof(dml::USHRT) + sizeof(dml::UINT) + - sizeof(dml::UINT) + sizeof(dml::UINT) + + sizeof(dml::INT) + sizeof(dml::UINT) + sizeof(dml::USHRT); } - - Packet *ClientHello::create_packet(uint16_t session_id, - uint32_t timestamp, uint32_t milliseconds) - { - const ClientHello data(session_id, timestamp, milliseconds); - auto *packet = new Packet(true, (uint8_t)Opcode::CLIENT_HELLO); - packet->set_payload_data(data); - return packet; - } } } } \ No newline at end of file diff --git a/src/protocol/control/Ping.cpp b/src/protocol/control/Ping.cpp index b5a2cd5..6260b6b 100644 --- a/src/protocol/control/Ping.cpp +++ b/src/protocol/control/Ping.cpp @@ -71,16 +71,6 @@ namespace control return sizeof(dml::USHRT) + sizeof(dml::USHRT) + sizeof(dml::UBYT); } - - Packet* Ping::create_packet(uint16_t session_id, - uint16_t milliseconds, uint8_t minutes, bool response) - { - const Ping data(session_id, milliseconds, minutes); - auto *packet = new Packet(true, - (uint8_t)(response ? Opcode::PING_RSP : Opcode::PING)); - packet->set_payload_data(data); - return packet; - } } } } \ No newline at end of file diff --git a/src/protocol/control/ServerHello.cpp b/src/protocol/control/ServerHello.cpp index 4be2c91..5f25d89 100644 --- a/src/protocol/control/ServerHello.cpp +++ b/src/protocol/control/ServerHello.cpp @@ -7,8 +7,8 @@ namespace protocol { namespace control { - ServerHello::ServerHello(uint16_t session_id, - uint32_t timestamp, uint32_t milliseconds) + ServerHello::ServerHello(const uint16_t session_id, + const int32_t timestamp, const uint32_t milliseconds) { m_session_id = session_id; m_timestamp = timestamp; @@ -20,17 +20,17 @@ namespace control return m_session_id; } - void ServerHello::set_session_id(uint16_t session_id) + void ServerHello::set_session_id(const uint16_t session_id) { m_session_id = session_id; } - uint32_t ServerHello::get_timestamp() const + int32_t ServerHello::get_timestamp() const { return m_timestamp; } - void ServerHello::set_timestamp(uint32_t timestamp) + void ServerHello::set_timestamp(const int32_t timestamp) { m_timestamp = timestamp; } @@ -40,7 +40,7 @@ namespace control return m_milliseconds; } - void ServerHello::set_milliseconds(uint32_t milliseconds) + void ServerHello::set_milliseconds(const uint32_t milliseconds) { m_milliseconds = milliseconds; } @@ -50,7 +50,7 @@ namespace control dml::Record record; record.add_field("m_session_id")->set_value(m_session_id); record.add_field("unknown"); - record.add_field("m_timestamp")->set_value(m_timestamp); + record.add_field("m_timestamp")->set_value(m_timestamp); record.add_field("m_milliseconds")->set_value(m_milliseconds); record.write_to(ostream); } @@ -60,7 +60,7 @@ namespace control dml::Record record; auto *session_id = record.add_field("m_session_id"); record.add_field("unknown"); - auto *timestamp = record.add_field("m_timestamp"); + auto *timestamp = record.add_field("m_timestamp"); auto *milliseconds = record.add_field("m_milliseconds"); record.read_from(istream); @@ -71,17 +71,8 @@ namespace control size_t ServerHello::get_size() const { - return sizeof(dml::USHRT) + sizeof(dml::GID) + - sizeof(dml::UINT); - } - - Packet *ServerHello::create_packet(uint16_t session_id, - uint32_t timestamp, uint32_t milliseconds) - { - const ServerHello data(session_id, timestamp, milliseconds); - auto *packet = new Packet(true, (uint8_t)Opcode::SERVER_HELLO); - packet->set_payload_data(data); - return packet; + return sizeof(dml::USHRT) + sizeof(dml::UINT) + + sizeof(dml::INT) + sizeof(dml::UINT); } } } From 57ef9b95f80fcec7083a48466b029f8d9fd98d90 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 9 Apr 2018 21:51:02 +0100 Subject: [PATCH 22/49] protocol: Implement PacketHeader --- include/ki/protocol/Packet.h | 66 -------------------------- include/ki/protocol/net/PacketHeader.h | 32 +++++++++++++ src/protocol/Packet.cpp | 63 ------------------------ src/protocol/net/PacketHeader.cpp | 64 +++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 129 deletions(-) delete mode 100644 include/ki/protocol/Packet.h create mode 100644 include/ki/protocol/net/PacketHeader.h delete mode 100644 src/protocol/Packet.cpp create mode 100644 src/protocol/net/PacketHeader.cpp diff --git a/include/ki/protocol/Packet.h b/include/ki/protocol/Packet.h deleted file mode 100644 index ef56c05..0000000 --- a/include/ki/protocol/Packet.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once -#include "../util/Serializable.h" -#include "exception.h" -#include -#include -#include -#include - -namespace ki -{ -namespace protocol -{ - class Packet final : public util::Serializable - { - public: - Packet(bool control = false, uint8_t opcode = 0); - virtual ~Packet() = default; - - bool is_control() const; - void set_control(bool control); - - uint8_t get_opcode() const; - void set_opcode(uint8_t opcode); - - template - void set_payload_data(const DataT &data) - { - static_assert(std::is_base_of::value, - "DataT must derive from Serializable."); - - std::ostringstream oss; - data.write_to(oss); - std::string data_string = oss.str(); - m_payload.assign(data_string.begin(), data_string.end()); - } - - template - DataT *get_payload_data() const - { - static_assert(std::is_base_of::value, - "DataT must derive from Serializable."); - - std::istringstream iss(std::string(m_payload.data(), m_payload.size())); - DataT *data = new DataT(); - try - { - data->read_from(iss); - } - catch (parse_error &e) - { - delete data; - throw; - } - return data; - } - - void write_to(std::ostream &ostream) const override final; - void read_from(std::istream &istream) override final; - size_t get_size() const override final; - private: - bool m_control; - uint8_t m_opcode; - std::vector m_payload; - }; -} -} diff --git a/include/ki/protocol/net/PacketHeader.h b/include/ki/protocol/net/PacketHeader.h new file mode 100644 index 0000000..702cb3b --- /dev/null +++ b/include/ki/protocol/net/PacketHeader.h @@ -0,0 +1,32 @@ +#pragma once +#include "../../util/Serializable.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace net +{ + class PacketHeader final : public util::Serializable + { + public: + PacketHeader(bool control = false, uint8_t opcode = 0); + virtual ~PacketHeader() = default; + + bool is_control() const; + void set_control(bool control); + + uint8_t get_opcode() const; + void set_opcode(uint8_t opcode); + + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; + private: + bool m_control; + uint8_t m_opcode; + }; +} +} +} \ No newline at end of file diff --git a/src/protocol/Packet.cpp b/src/protocol/Packet.cpp deleted file mode 100644 index 1223793..0000000 --- a/src/protocol/Packet.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "ki/protocol/Packet.h" - -namespace ki -{ -namespace protocol -{ - Packet::Packet(bool control, uint8_t opcode) - { - m_control = control; - m_opcode = opcode; - m_payload = std::vector(); - } - - bool Packet::is_control() const - { - return m_control; - } - - void Packet::set_control(bool control) - { - m_control = control; - } - - uint8_t Packet::get_opcode() const - { - return m_opcode; - } - - void Packet::set_opcode(uint8_t opcode) - { - m_opcode = opcode; - } - - void Packet::write_to(std::ostream& ostream) const - { - ostream.put(m_control); - ostream.put(m_opcode); - ostream.put(0); - ostream.put(0); - ostream.write(m_payload.data(), m_payload.size()); - } - - void Packet::read_from(std::istream& istream) - { - m_control = istream.get() >= 1; - m_opcode = istream.get(); - istream.ignore(2); - - const std::ios::pos_type pos = istream.tellg(); - istream.seekg(0, std::ios::end); - const size_t size = istream.tellg() - pos; - istream.seekg(pos, std::ios::beg); - - m_payload.resize(size); - istream.read(m_payload.data(), size); - } - - size_t Packet::get_size() const - { - return 4 + m_payload.size(); - } -} -} diff --git a/src/protocol/net/PacketHeader.cpp b/src/protocol/net/PacketHeader.cpp new file mode 100644 index 0000000..db64ae0 --- /dev/null +++ b/src/protocol/net/PacketHeader.cpp @@ -0,0 +1,64 @@ +#include "ki/protocol/net/PacketHeader.h" +#include "ki/protocol/exception.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace net +{ + PacketHeader::PacketHeader(const bool control, const uint8_t opcode) + { + m_control = control; + m_opcode = opcode; + } + + bool PacketHeader::is_control() const + { + return m_control; + } + + void PacketHeader::set_control(const bool control) + { + m_control = control; + } + + uint8_t PacketHeader::get_opcode() const + { + return m_opcode; + } + + void PacketHeader::set_opcode(const uint8_t opcode) + { + m_opcode = opcode; + } + + void PacketHeader::write_to(std::ostream& ostream) const + { + ostream.put(m_control); + ostream.put(m_opcode); + ostream.put(0); + ostream.put(0); + } + + void PacketHeader::read_from(std::istream& istream) + { + m_control = istream.get() >= 1; + if (istream.fail()) + throw parse_error("Not enough data was available to read packet header. (m_control)"); + m_opcode = istream.get(); + if (istream.fail()) + throw parse_error("Not enough data was available to read packet header. (m_opcode)"); + istream.ignore(2); + if (istream.fail()) + throw parse_error("Not enough data was available to read packet header. (ignored bytes)"); + } + + size_t PacketHeader::get_size() const + { + return 4; + } +} +} +} From 5ff4fb651d26e0414af167c98062e886c4c8e0f8 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 9 Apr 2018 21:53:09 +0100 Subject: [PATCH 23/49] protocol: Add Participant and Session --- include/ki/protocol/net/Participant.h | 77 ++++++++ include/ki/protocol/net/Session.h | 88 +++++++++ src/protocol/CMakeLists.txt | 4 +- src/protocol/net/Participant.cpp | 137 +++++++++++++ src/protocol/net/Session.cpp | 273 ++++++++++++++++++++++++++ 5 files changed, 578 insertions(+), 1 deletion(-) create mode 100644 include/ki/protocol/net/Participant.h create mode 100644 include/ki/protocol/net/Session.h create mode 100644 src/protocol/net/Participant.cpp create mode 100644 src/protocol/net/Session.cpp diff --git a/include/ki/protocol/net/Participant.h b/include/ki/protocol/net/Participant.h new file mode 100644 index 0000000..36fb876 --- /dev/null +++ b/include/ki/protocol/net/Participant.h @@ -0,0 +1,77 @@ +#pragma once +#include +#include + +#define KI_DEFAULT_MAXIMUM_RECEIVE_SIZE 0x2000 +#define KI_START_SIGNAL 0xF00D + +namespace ki +{ +namespace protocol +{ +namespace net +{ + enum class ReceiveState + { + // Waiting for the 0xF00D start signal. + WAITING_FOR_START_SIGNAL, + + // Waiting for the 2-byte length. + WAITING_FOR_LENGTH, + + // Waiting for the packet data. + WAITING_FOR_PACKET + }; + + enum class ParticipantType + { + SERVER, + CLIENT + }; + + /** + * This class implements the packet framing logic when + * sending and receiving data to/from an external source. + */ + class Participant + { + public: + Participant(ParticipantType type); + virtual ~Participant() = default; + + ParticipantType get_type() const; + void set_type(ParticipantType type); + + uint16_t get_maximum_packet_size() const; + void set_maximum_packet_size(uint16_t maximum_packet_size); + protected: + std::stringstream m_data_stream; + + /** + * Frames raw data into a Packet, and transmits it. + */ + void send_data(const char *data, size_t size); + + /** + * Process incoming raw data into Packets. + * Once a packet is read into the internal data + * stream, handle_packet_available is called. + */ + void process_data(const char *data, size_t size); + + virtual void close() = 0; + private: + ParticipantType m_type; + uint16_t m_maximum_packet_size; + + ReceiveState m_receive_state; + uint16_t m_start_signal; + uint16_t m_incoming_packet_size; + uint8_t m_shift; + + virtual void send_packet_data(const char *data, const size_t size) = 0; + virtual void on_packet_available() {}; + }; +} +} +} diff --git a/include/ki/protocol/net/Session.h b/include/ki/protocol/net/Session.h new file mode 100644 index 0000000..4b43479 --- /dev/null +++ b/include/ki/protocol/net/Session.h @@ -0,0 +1,88 @@ +#pragma once +#include "Participant.h" +#include "PacketHeader.h" +#include "ki/protocol/control/Opcode.h" +#include "../../util/Serializable.h" +#include +#include +#include + +namespace ki +{ +namespace protocol +{ +namespace net +{ + /** + * This class implements session logic on top of the + * low-level Participant class. + */ + class Session : public Participant + { + public: + Session(ParticipantType type, uint16_t id); + + uint16_t get_id() const; + bool is_established() const; + + uint8_t get_access_level() const; + void set_access_level(uint8_t access_level); + + uint16_t get_latency() const; + + bool is_alive() const; + protected: + template + void send_packet(const bool is_control, const control::Opcode opcode, + const DataT &data) + { + static_assert(std::is_base_of::value, + "DataT must inherit Serializable."); + + std::ostringstream ss; + PacketHeader header(is_control, (uint8_t)opcode); + header.write_to(ss); + data.write_to(ss); + + const auto buffer = ss.str(); + send_data(buffer.c_str(), buffer.length()); + } + + template + DataT read_data() + { + static_assert(std::is_base_of::value, + "DataT must inherit Serializable."); + + DataT data = DataT(); + data.read_from(m_data_stream); + return data; + } + + void on_connected(); + virtual void on_established() {}; + virtual void on_application_message(const PacketHeader &header) {}; + virtual void on_invalid_packet() {}; + private: + uint16_t m_id; + bool m_established; + uint8_t m_access_level; + uint16_t m_latency; + + std::chrono::steady_clock::time_point m_creation_time; + std::chrono::steady_clock::time_point m_establish_time; + std::chrono::steady_clock::time_point m_last_heartbeat; + + void on_packet_available() override final; + void on_control_message(const PacketHeader &header); + void on_server_hello(); + void on_client_hello(); + void on_ping(); + void on_ping_response(); + + void on_hello(uint16_t session_id, uint32_t timestamp, + uint16_t milliseconds); + }; +} +} +} diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt index 2d6393a..e59aea3 100644 --- a/src/protocol/CMakeLists.txt +++ b/src/protocol/CMakeLists.txt @@ -1,6 +1,5 @@ target_sources(${PROJECT_NAME} PRIVATE - ${PROJECT_SOURCE_DIR}/src/protocol/Packet.cpp ${PROJECT_SOURCE_DIR}/src/protocol/control/ClientHello.cpp ${PROJECT_SOURCE_DIR}/src/protocol/control/ServerHello.cpp ${PROJECT_SOURCE_DIR}/src/protocol/control/Ping.cpp @@ -9,4 +8,7 @@ target_sources(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageManager.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageModule.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageTemplate.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/net/PacketHeader.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/net/Participant.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/net/Session.cpp ) \ No newline at end of file diff --git a/src/protocol/net/Participant.cpp b/src/protocol/net/Participant.cpp new file mode 100644 index 0000000..d5db22b --- /dev/null +++ b/src/protocol/net/Participant.cpp @@ -0,0 +1,137 @@ +#include "ki/protocol/net/Participant.h" +#include "ki/protocol/exception.h" + +namespace ki +{ +namespace protocol +{ +namespace net +{ + Participant::Participant(const ParticipantType type) + { + m_type = type; + m_maximum_packet_size = KI_DEFAULT_MAXIMUM_RECEIVE_SIZE; + + m_receive_state = ReceiveState::WAITING_FOR_START_SIGNAL; + m_start_signal = 0; + m_incoming_packet_size = 0; + m_shift = 0; + } + + ParticipantType Participant::get_type() const + { + return m_type; + } + + void Participant::set_type(const ParticipantType type) + { + m_type = type; + } + + + uint16_t Participant::get_maximum_packet_size() const + { + return m_maximum_packet_size; + } + + void Participant::set_maximum_packet_size(const uint16_t maximum_packet_size) + { + m_maximum_packet_size = maximum_packet_size; + } + + void Participant::send_data(const char* data, const size_t size) + { + // Allocate the entire buffer + char *packet_data = new char[size + 4]; + + // Add the frame header + ((uint16_t *)packet_data)[0] = KI_START_SIGNAL; + ((uint16_t *)packet_data)[1] = size; + + // Copy the payload into the buffer and send it + memcpy(&packet_data[4], data, size); + send_packet_data(packet_data, size + 4); + delete[] packet_data; + } + + void Participant::process_data(const char *data, const size_t size) + { + size_t position = 0; + while (position < size) + { + switch (m_receive_state) + { + case ReceiveState::WAITING_FOR_START_SIGNAL: + m_start_signal |= ((uint8_t)data[position] << m_shift); + if (m_shift == 0) + m_shift = 8; + else + { + // If the start signal isn't correct, we've either + // gotten out of sync, or they are not framing packets + // correctly. + if (m_start_signal != KI_START_SIGNAL) + { + close(); + return; + } + + // Reset the shift and incoming packet size + m_shift = 0; + m_incoming_packet_size = 0; + m_receive_state = ReceiveState::WAITING_FOR_LENGTH; + } + position++; + break; + + case ReceiveState::WAITING_FOR_LENGTH: + m_incoming_packet_size |= ((uint8_t)data[position] << m_shift); + if (m_shift == 0) + m_shift = 8; + else + { + // If the incoming packet is larger than we are accepting + // stop processing data. + if (m_incoming_packet_size > m_maximum_packet_size) + { + close(); + return; + } + + // Reset read and write positions + m_data_stream.seekp(0, std::ios::beg); + m_data_stream.seekg(0, std::ios::beg); + m_receive_state = ReceiveState::WAITING_FOR_PACKET; + } + position++; + break; + + case ReceiveState::WAITING_FOR_PACKET: + // Work out how much data we should read into our stream + const size_t data_available = (size - position); + const size_t read_size = (data_available >= m_incoming_packet_size) ? + m_incoming_packet_size : data_available; + + // Write the data to the data stream + m_data_stream.write(&data[position], read_size); + position += read_size; + m_incoming_packet_size -= read_size; + + // Have we received the entire packet? + if (m_incoming_packet_size == 0) + { + on_packet_available(); + + // Reset the shift and start signal + m_shift = 0; + m_start_signal = 0; + m_receive_state = ReceiveState::WAITING_FOR_START_SIGNAL; + } + break; + } + } + } + +} +} +} diff --git a/src/protocol/net/Session.cpp b/src/protocol/net/Session.cpp new file mode 100644 index 0000000..9238fb4 --- /dev/null +++ b/src/protocol/net/Session.cpp @@ -0,0 +1,273 @@ +#include "ki/protocol/net/Session.h" +#include "ki/protocol/exception.h" +#include "ki/protocol/control/ServerHello.h" +#include "ki/protocol/control/ClientHello.h" +#include "ki/protocol/control/Ping.h" + +namespace ki +{ +namespace protocol +{ +namespace net +{ + Session::Session(const ParticipantType type, const uint16_t id) + : Participant(type) + { + m_id = id; + m_established = false; + m_access_level = 0; + m_latency = 0; + m_creation_time = std::chrono::steady_clock::now(); + } + + uint16_t Session::get_id() const + { + return m_id; + } + + bool Session::is_established() const + { + return m_established; + } + + uint8_t Session::get_access_level() const + { + return m_access_level; + } + + void Session::set_access_level(const uint8_t access_level) + { + m_access_level = access_level; + } + + uint16_t Session::get_latency() const + { + return m_latency; + } + + bool Session::is_alive() const + { + // If the session isn't established yet, use the time of + // creation to decide whether this session is alive. + if (!m_established) + return std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_creation_time + ).count() <= 3; + + // Otherwise, use the last time we received a heartbeat. + return std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_last_heartbeat + ).count() <= 10; + } + + void Session::on_connected() + { + // If this is the server-side of a Session + // we need to send SERVER_HELLO first. + if (get_type() == ParticipantType::SERVER) + { + // Work out the current timestamp and how many milliseconds + // have elapsed in the current second. + auto now = std::chrono::system_clock::now(); + const auto timestamp = std::chrono::duration_cast( + now.time_since_epoch() + ).count(); + const auto milliseconds = std::chrono::duration_cast( + now.time_since_epoch() + ).count() - (timestamp * 1000); + + // Send a SERVER_HELLO packet to the client + const control::ServerHello hello(m_id, timestamp, milliseconds); + send_packet( + true, control::Opcode::SERVER_HELLO, hello); + } + } + + void Session::on_packet_available() + { + // Read the packet header + PacketHeader header; + try + { + header.read_from(m_data_stream); + } + catch (parse_error &e) + { + on_invalid_packet(); + return; + } + + // Hand off to the right handler based on + // whether this is a control packet or not + if (header.is_control()) + on_control_message(header); + else + on_application_message(header); + } + + void Session::on_control_message(const PacketHeader& header) + { + switch ((control::Opcode)header.get_opcode()) + { + case (control::Opcode::SERVER_HELLO): + on_server_hello(); + break; + + case (control::Opcode::CLIENT_HELLO): + on_client_hello(); + break; + + case (control::Opcode::PING): + on_ping(); + break; + + case (control::Opcode::PING_RSP): + on_ping_response(); + break; + + default: + break; + } + } + + void Session::on_server_hello() + { + // If this is the server-side of a Session + // we can't handle a SERVER_HELLO + if (get_type() != ParticipantType::CLIENT) + { + close(); + return; + } + + // Read the payload data into a structure + try + { + // We've been given our id from the server now + const auto server_hello = read_data(); + m_id = server_hello.get_session_id(); + on_hello(m_id, + server_hello.get_timestamp(), + server_hello.get_milliseconds()); + + // Work out the current timestamp and how many milliseconds + // have elapsed in the current second. + auto now = std::chrono::system_clock::now(); + const auto timestamp = std::chrono::duration_cast( + now.time_since_epoch() + ).count(); + const auto milliseconds = std::chrono::duration_cast( + now.time_since_epoch() + ).count() - (timestamp * 1000); + + // Send a CLIENT_HELLO packet to the server + const control::ClientHello hello(m_id, timestamp, milliseconds); + send_packet( + true, control::Opcode::CLIENT_HELLO, hello); + } + catch (parse_error &e) + { + // The CLIENT_HELLO wasn't valid... + // Close the session + close(); + } + } + + void Session::on_client_hello() + { + // If this is the client-side of a Session + // we can't handle a CLIENT_HELLO + if (get_type() != ParticipantType::SERVER) + { + close(); + return; + } + + // Read the payload data into a structure + try + { + // The session is now established! + const auto client_hello = read_data(); + on_hello(client_hello.get_session_id(), + client_hello.get_timestamp(), + client_hello.get_milliseconds()); + } + catch (parse_error &e) + { + // The CLIENT_HELLO wasn't valid... + // Close the session + close(); + } + } + + void Session::on_ping() + { + // Read the payload data into a structure + try + { + const auto ping = read_data(); + if (get_type() == ParticipantType::SERVER) + { + // Calculate latency + const auto send_time = m_establish_time + + std::chrono::milliseconds(ping.get_milliseconds()) + + std::chrono::minutes(ping.get_minutes()); + m_latency = std::chrono::duration_cast( + std::chrono::steady_clock::now() - send_time + ).count(); + } + + // Send the response + send_packet( + true, control::Opcode::PING_RSP, ping); + } + catch (parse_error &e) + { + // The CLIENT_HELLO wasn't valid... + // Close the session + close(); + } + } + + void Session::on_ping_response() + { + // Read the payload data into a structure + try + { + const auto ping = read_data(); + } + catch (parse_error &e) + { + // The CLIENT_HELLO wasn't valid... + // Close the session + close(); + } + } + + void Session::on_hello(const uint16_t session_id, + const uint32_t timestamp, const uint16_t milliseconds) + { + // Make sure they're accepting this session + if (session_id != m_id) + { + close(); + return; + } + + // Calculate initial latency + const std::chrono::system_clock::time_point epoch; + const auto send_time = epoch + (std::chrono::seconds(timestamp) + + std::chrono::milliseconds(milliseconds)); + m_latency = std::chrono::duration_cast( + std::chrono::system_clock::now() - send_time + ).count(); + + // The session is successfully established + m_established = true; + m_establish_time = std::chrono::steady_clock::now(); + m_last_heartbeat = m_establish_time; + on_established(); + } +} +} +} From 1755bda42eff2a41c3750923ecb17877aab8efd7 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 9 Apr 2018 21:56:58 +0100 Subject: [PATCH 24/49] protocol: Fix memcpy build error --- src/protocol/net/Participant.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/protocol/net/Participant.cpp b/src/protocol/net/Participant.cpp index d5db22b..8e1fb73 100644 --- a/src/protocol/net/Participant.cpp +++ b/src/protocol/net/Participant.cpp @@ -1,5 +1,6 @@ #include "ki/protocol/net/Participant.h" #include "ki/protocol/exception.h" +#include namespace ki { @@ -49,7 +50,7 @@ namespace net ((uint16_t *)packet_data)[1] = size; // Copy the payload into the buffer and send it - memcpy(&packet_data[4], data, size); + std::memcpy(&packet_data[4], data, size); send_packet_data(packet_data, size + 4); delete[] packet_data; } From e89cdd2ae40c66ae881eef3f08f088a57dc8f1b9 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 9 Apr 2018 22:09:49 +0100 Subject: [PATCH 25/49] protocol: Don't handle application messages before CLIENT_HELLO --- src/protocol/net/Session.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/protocol/net/Session.cpp b/src/protocol/net/Session.cpp index 9238fb4..3b1dd24 100644 --- a/src/protocol/net/Session.cpp +++ b/src/protocol/net/Session.cpp @@ -101,8 +101,10 @@ namespace net // whether this is a control packet or not if (header.is_control()) on_control_message(header); - else + else if (m_established) on_application_message(header); + else + close(); } void Session::on_control_message(const PacketHeader& header) From 70679afed1eb732c075d88ab6642e2ea02c24fcc Mon Sep 17 00:00:00 2001 From: Pythonology Date: Mon, 9 Apr 2018 22:34:50 -0400 Subject: [PATCH 26/49] net: send_packet_data() needs to be protected This is required in order to expose it to Python. --- include/ki/protocol/net/Participant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ki/protocol/net/Participant.h b/include/ki/protocol/net/Participant.h index 36fb876..826e8aa 100644 --- a/include/ki/protocol/net/Participant.h +++ b/include/ki/protocol/net/Participant.h @@ -59,6 +59,7 @@ namespace net */ void process_data(const char *data, size_t size); + virtual void send_packet_data(const char *data, const size_t size) = 0; virtual void close() = 0; private: ParticipantType m_type; @@ -69,7 +70,6 @@ namespace net uint16_t m_incoming_packet_size; uint8_t m_shift; - virtual void send_packet_data(const char *data, const size_t size) = 0; virtual void on_packet_available() {}; }; } From e62f3e7647e45927c1e52fe8a96eba85a043f33f Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Wed, 11 Apr 2018 00:35:16 +0100 Subject: [PATCH 27/49] protocol: Implement a DML-based Session --- include/ki/protocol/dml/Session.h | 26 ++++++++++++++++++++++++++ src/protocol/CMakeLists.txt | 1 + src/protocol/dml/Session.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 include/ki/protocol/dml/Session.h create mode 100644 src/protocol/dml/Session.cpp diff --git a/include/ki/protocol/dml/Session.h b/include/ki/protocol/dml/Session.h new file mode 100644 index 0000000..a48120e --- /dev/null +++ b/include/ki/protocol/dml/Session.h @@ -0,0 +1,26 @@ +#pragma once +#include "../net/Session.h" +#include "MessageManager.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class Session : public net::Session + { + public: + Session(net::ParticipantType type, uint16_t id, + const MessageManager &manager); + ~Session() = default; + protected: + void on_application_message(const net::PacketHeader& header) override; + virtual void on_message(const Message &message) {} + virtual void on_invalid_message() {} + private: + const MessageManager &m_manager; + }; +} +} +} \ No newline at end of file diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt index e59aea3..660ab30 100644 --- a/src/protocol/CMakeLists.txt +++ b/src/protocol/CMakeLists.txt @@ -8,6 +8,7 @@ target_sources(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageManager.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageModule.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageTemplate.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/Session.cpp ${PROJECT_SOURCE_DIR}/src/protocol/net/PacketHeader.cpp ${PROJECT_SOURCE_DIR}/src/protocol/net/Participant.cpp ${PROJECT_SOURCE_DIR}/src/protocol/net/Session.cpp diff --git a/src/protocol/dml/Session.cpp b/src/protocol/dml/Session.cpp new file mode 100644 index 0000000..fc513b6 --- /dev/null +++ b/src/protocol/dml/Session.cpp @@ -0,0 +1,27 @@ +#include "ki/protocol/dml/Session.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + Session::Session(const net::ParticipantType type, const uint16_t id, + const MessageManager& manager) + : net::Session(type, id), m_manager(manager) {} + + void Session::on_application_message(const net::PacketHeader& header) + { + const auto *message = m_manager.message_from_binary(m_data_stream); + if (!message) + { + on_invalid_message(); + return; + } + + on_message(*message); + delete message; + } +} +} +} \ No newline at end of file From 88f98fd84c63307e7bdfd4cc2ef2c9926ce28659 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Wed, 11 Apr 2018 00:38:33 +0100 Subject: [PATCH 28/49] net: Fix small bug in PacketHeader This prevented on_invalid_packet from being called --- src/protocol/net/PacketHeader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol/net/PacketHeader.cpp b/src/protocol/net/PacketHeader.cpp index db64ae0..e1091f8 100644 --- a/src/protocol/net/PacketHeader.cpp +++ b/src/protocol/net/PacketHeader.cpp @@ -51,7 +51,7 @@ namespace net if (istream.fail()) throw parse_error("Not enough data was available to read packet header. (m_opcode)"); istream.ignore(2); - if (istream.fail()) + if (istream.eof()) throw parse_error("Not enough data was available to read packet header. (ignored bytes)"); } From 599afed56f0d008754c583b68280a897aa5fd290 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Wed, 11 Apr 2018 00:50:10 +0100 Subject: [PATCH 29/49] control: Catch DML errors when parsing control messages --- src/protocol/control/ClientHello.cpp | 14 ++++++++++++-- src/protocol/control/Ping.cpp | 14 ++++++++++++-- src/protocol/control/ServerHello.cpp | 14 ++++++++++++-- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/protocol/control/ClientHello.cpp b/src/protocol/control/ClientHello.cpp index 6d6e819..007ec21 100644 --- a/src/protocol/control/ClientHello.cpp +++ b/src/protocol/control/ClientHello.cpp @@ -1,5 +1,6 @@ #include "ki/protocol/control/ClientHello.h" #include "ki/dml/Record.h" +#include "ki/protocol/exception.h" namespace ki { @@ -64,7 +65,16 @@ namespace control auto *timestamp = record.add_field("m_timestamp"); auto *milliseconds = record.add_field("m_milliseconds"); auto *session_id = record.add_field("m_session_id"); - record.read_from(istream); + try + { + record.read_from(istream); + } + catch (dml::parse_error &e) + { + std::ostringstream oss; + oss << "Error reading ClientHello payload: " << e.what(); + throw parse_error(oss.str()); + } m_timestamp = timestamp->get_value(); m_milliseconds = milliseconds->get_value(); @@ -79,4 +89,4 @@ namespace control } } } -} \ No newline at end of file +} diff --git a/src/protocol/control/Ping.cpp b/src/protocol/control/Ping.cpp index 6260b6b..5e7987b 100644 --- a/src/protocol/control/Ping.cpp +++ b/src/protocol/control/Ping.cpp @@ -1,5 +1,6 @@ #include "ki/protocol/control/Ping.h" #include "ki/dml/Record.h" +#include "ki/protocol/exception.h" namespace ki { @@ -59,7 +60,16 @@ namespace control auto *session_id = record.add_field("m_session_id"); auto *milliseconds = record.add_field("m_milliseconds"); auto *minutes = record.add_field("m_minutes"); - record.read_from(istream); + try + { + record.read_from(istream); + } + catch (dml::parse_error &e) + { + std::ostringstream oss; + oss << "Error reading Ping payload: " << e.what(); + throw parse_error(oss.str()); + } m_session_id = session_id->get_value(); m_milliseconds = milliseconds->get_value(); @@ -73,4 +83,4 @@ namespace control } } } -} \ No newline at end of file +} diff --git a/src/protocol/control/ServerHello.cpp b/src/protocol/control/ServerHello.cpp index 5f25d89..450a5b5 100644 --- a/src/protocol/control/ServerHello.cpp +++ b/src/protocol/control/ServerHello.cpp @@ -1,5 +1,6 @@ #include "ki/protocol/control/ServerHello.h" #include "ki/dml/Record.h" +#include "ki/protocol/exception.h" namespace ki { @@ -62,7 +63,16 @@ namespace control record.add_field("unknown"); auto *timestamp = record.add_field("m_timestamp"); auto *milliseconds = record.add_field("m_milliseconds"); - record.read_from(istream); + try + { + record.read_from(istream); + } + catch (dml::parse_error &e) + { + std::ostringstream oss; + oss << "Error reading ServerHello payload: " << e.what(); + throw parse_error(oss.str()); + } m_session_id = session_id->get_value(); m_timestamp = timestamp->get_value(); @@ -76,4 +86,4 @@ namespace control } } } -} \ No newline at end of file +} From c4630ae970f5d6c0c564327edd380c7b55cefbc9 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Wed, 11 Apr 2018 01:12:53 +0100 Subject: [PATCH 30/49] net: Session::send_packet was unnecessarily templated --- include/ki/protocol/net/Session.h | 19 +++---------------- src/protocol/net/Session.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/include/ki/protocol/net/Session.h b/include/ki/protocol/net/Session.h index 4b43479..fe97b1d 100644 --- a/include/ki/protocol/net/Session.h +++ b/include/ki/protocol/net/Session.h @@ -31,23 +31,10 @@ namespace net uint16_t get_latency() const; bool is_alive() const; + + void send_packet(bool is_control, control::Opcode opcode, + const util::Serializable &data); protected: - template - void send_packet(const bool is_control, const control::Opcode opcode, - const DataT &data) - { - static_assert(std::is_base_of::value, - "DataT must inherit Serializable."); - - std::ostringstream ss; - PacketHeader header(is_control, (uint8_t)opcode); - header.write_to(ss); - data.write_to(ss); - - const auto buffer = ss.str(); - send_data(buffer.c_str(), buffer.length()); - } - template DataT read_data() { diff --git a/src/protocol/net/Session.cpp b/src/protocol/net/Session.cpp index 3b1dd24..9e5e77b 100644 --- a/src/protocol/net/Session.cpp +++ b/src/protocol/net/Session.cpp @@ -60,6 +60,18 @@ namespace net ).count() <= 10; } + void Session::send_packet(const bool is_control, const control::Opcode opcode, + const util::Serializable& data) + { + std::ostringstream ss; + PacketHeader header(is_control, (uint8_t)opcode); + header.write_to(ss); + data.write_to(ss); + + const auto buffer = ss.str(); + send_data(buffer.c_str(), buffer.length()); + } + void Session::on_connected() { // If this is the server-side of a Session From 2acb22214c35537cd7001f7dcef5859e54ce8fb3 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Wed, 11 Apr 2018 01:13:37 +0100 Subject: [PATCH 31/49] net: Rename dml::Session to net::DMLSession --- .../ki/protocol/{dml/Session.h => net/DMLSession.h} | 10 +++++----- src/protocol/CMakeLists.txt | 2 +- src/protocol/{dml/Session.cpp => net/DMLSession.cpp} | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) rename include/ki/protocol/{dml/Session.h => net/DMLSession.h} (65%) rename src/protocol/{dml/Session.cpp => net/DMLSession.cpp} (62%) diff --git a/include/ki/protocol/dml/Session.h b/include/ki/protocol/net/DMLSession.h similarity index 65% rename from include/ki/protocol/dml/Session.h rename to include/ki/protocol/net/DMLSession.h index a48120e..f2cfd61 100644 --- a/include/ki/protocol/dml/Session.h +++ b/include/ki/protocol/net/DMLSession.h @@ -1,6 +1,6 @@ #pragma once -#include "../net/Session.h" -#include "MessageManager.h" +#include "Session.h" +#include "../dml/MessageManager.h" namespace ki { @@ -8,12 +8,12 @@ namespace protocol { namespace dml { - class Session : public net::Session + class DMLSession : public net::Session { public: - Session(net::ParticipantType type, uint16_t id, + DMLSession(net::ParticipantType type, uint16_t id, const MessageManager &manager); - ~Session() = default; + ~DMLSession() = default; protected: void on_application_message(const net::PacketHeader& header) override; virtual void on_message(const Message &message) {} diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt index 660ab30..c3e5e38 100644 --- a/src/protocol/CMakeLists.txt +++ b/src/protocol/CMakeLists.txt @@ -8,8 +8,8 @@ target_sources(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageManager.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageModule.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageTemplate.cpp - ${PROJECT_SOURCE_DIR}/src/protocol/dml/Session.cpp ${PROJECT_SOURCE_DIR}/src/protocol/net/PacketHeader.cpp ${PROJECT_SOURCE_DIR}/src/protocol/net/Participant.cpp ${PROJECT_SOURCE_DIR}/src/protocol/net/Session.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/net/DMLSession.cpp ) \ No newline at end of file diff --git a/src/protocol/dml/Session.cpp b/src/protocol/net/DMLSession.cpp similarity index 62% rename from src/protocol/dml/Session.cpp rename to src/protocol/net/DMLSession.cpp index fc513b6..80a9b32 100644 --- a/src/protocol/dml/Session.cpp +++ b/src/protocol/net/DMLSession.cpp @@ -1,4 +1,4 @@ -#include "ki/protocol/dml/Session.h" +#include "ki/protocol/net/DMLSession.h" namespace ki { @@ -6,11 +6,11 @@ namespace protocol { namespace dml { - Session::Session(const net::ParticipantType type, const uint16_t id, + DMLSession::DMLSession(const net::ParticipantType type, const uint16_t id, const MessageManager& manager) : net::Session(type, id), m_manager(manager) {} - void Session::on_application_message(const net::PacketHeader& header) + void DMLSession::on_application_message(const net::PacketHeader& header) { const auto *message = m_manager.message_from_binary(m_data_stream); if (!message) @@ -24,4 +24,4 @@ namespace dml } } } -} \ No newline at end of file +} From 1d6855581a8ad3684e2eb02fe8af01a5c767f476 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Wed, 11 Apr 2018 01:16:21 +0100 Subject: [PATCH 32/49] net: Fix namespace typo in DMLSession Forgot to change these to net after moving them from dml --- include/ki/protocol/net/DMLSession.h | 14 +++++++------- src/protocol/net/DMLSession.cpp | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/ki/protocol/net/DMLSession.h b/include/ki/protocol/net/DMLSession.h index f2cfd61..0963db1 100644 --- a/include/ki/protocol/net/DMLSession.h +++ b/include/ki/protocol/net/DMLSession.h @@ -6,20 +6,20 @@ namespace ki { namespace protocol { -namespace dml +namespace net { - class DMLSession : public net::Session + class DMLSession : public Session { public: - DMLSession(net::ParticipantType type, uint16_t id, - const MessageManager &manager); + DMLSession(ParticipantType type, uint16_t id, + const dml::MessageManager &manager); ~DMLSession() = default; protected: - void on_application_message(const net::PacketHeader& header) override; - virtual void on_message(const Message &message) {} + void on_application_message(const PacketHeader& header) override; + virtual void on_message(const dml::Message &message) {} virtual void on_invalid_message() {} private: - const MessageManager &m_manager; + const dml::MessageManager &m_manager; }; } } diff --git a/src/protocol/net/DMLSession.cpp b/src/protocol/net/DMLSession.cpp index 80a9b32..a794ca8 100644 --- a/src/protocol/net/DMLSession.cpp +++ b/src/protocol/net/DMLSession.cpp @@ -4,13 +4,13 @@ namespace ki { namespace protocol { -namespace dml +namespace net { - DMLSession::DMLSession(const net::ParticipantType type, const uint16_t id, - const MessageManager& manager) - : net::Session(type, id), m_manager(manager) {} + DMLSession::DMLSession(const ParticipantType type, const uint16_t id, + const dml::MessageManager& manager) + : Session(type, id), m_manager(manager) {} - void DMLSession::on_application_message(const net::PacketHeader& header) + void DMLSession::on_application_message(const PacketHeader& header) { const auto *message = m_manager.message_from_binary(m_data_stream); if (!message) From 59aa21b4b6a5f45aa3d9efa8ad06972bfde63b27 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Wed, 11 Apr 2018 01:18:31 +0100 Subject: [PATCH 33/49] net: Implement send_message on DMLSession --- include/ki/protocol/control/Opcode.h | 1 + include/ki/protocol/net/DMLSession.h | 2 ++ src/protocol/net/DMLSession.cpp | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/include/ki/protocol/control/Opcode.h b/include/ki/protocol/control/Opcode.h index 8442a56..85ee88e 100644 --- a/include/ki/protocol/control/Opcode.h +++ b/include/ki/protocol/control/Opcode.h @@ -9,6 +9,7 @@ namespace control { enum class Opcode : uint8_t { + NONE = 0, SERVER_HELLO = 0, UDP_HELLO = 1, PING = 3, diff --git a/include/ki/protocol/net/DMLSession.h b/include/ki/protocol/net/DMLSession.h index 0963db1..3b0c66e 100644 --- a/include/ki/protocol/net/DMLSession.h +++ b/include/ki/protocol/net/DMLSession.h @@ -14,6 +14,8 @@ namespace net DMLSession(ParticipantType type, uint16_t id, const dml::MessageManager &manager); ~DMLSession() = default; + + void send_message(const dml::Message &message); protected: void on_application_message(const PacketHeader& header) override; virtual void on_message(const dml::Message &message) {} diff --git a/src/protocol/net/DMLSession.cpp b/src/protocol/net/DMLSession.cpp index a794ca8..de524ed 100644 --- a/src/protocol/net/DMLSession.cpp +++ b/src/protocol/net/DMLSession.cpp @@ -10,6 +10,11 @@ namespace net const dml::MessageManager& manager) : Session(type, id), m_manager(manager) {} + void DMLSession::send_message(const dml::Message& message) + { + send_packet(false, control::Opcode::NONE, message); + } + void DMLSession::on_application_message(const PacketHeader& header) { const auto *message = m_manager.message_from_binary(m_data_stream); From eeb5b5c14b8171d1c1018d5ae1b7e43cb07d1333 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Wed, 11 Apr 2018 01:20:23 +0100 Subject: [PATCH 34/49] net: Make Participant::on_packet_available protected Technically we don't want derived classes to be able to call this, but it's needed for the Python side to be able to override it. --- include/ki/protocol/net/Participant.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/ki/protocol/net/Participant.h b/include/ki/protocol/net/Participant.h index 826e8aa..67a11aa 100644 --- a/include/ki/protocol/net/Participant.h +++ b/include/ki/protocol/net/Participant.h @@ -60,6 +60,7 @@ namespace net void process_data(const char *data, size_t size); virtual void send_packet_data(const char *data, const size_t size) = 0; + virtual void on_packet_available() {}; virtual void close() = 0; private: ParticipantType m_type; @@ -69,8 +70,6 @@ namespace net uint16_t m_start_signal; uint16_t m_incoming_packet_size; uint8_t m_shift; - - virtual void on_packet_available() {}; }; } } From 00b404f13277eb93b41e34bafe15a8a7f9d49d41 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Wed, 11 Apr 2018 02:55:01 +0100 Subject: [PATCH 35/49] net: Fix usages of send_packet --- src/protocol/net/Session.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/protocol/net/Session.cpp b/src/protocol/net/Session.cpp index 9e5e77b..1cfa814 100644 --- a/src/protocol/net/Session.cpp +++ b/src/protocol/net/Session.cpp @@ -90,8 +90,7 @@ namespace net // Send a SERVER_HELLO packet to the client const control::ServerHello hello(m_id, timestamp, milliseconds); - send_packet( - true, control::Opcode::SERVER_HELLO, hello); + send_packet(true, control::Opcode::SERVER_HELLO, hello); } } @@ -176,8 +175,7 @@ namespace net // Send a CLIENT_HELLO packet to the server const control::ClientHello hello(m_id, timestamp, milliseconds); - send_packet( - true, control::Opcode::CLIENT_HELLO, hello); + send_packet(true, control::Opcode::CLIENT_HELLO, hello); } catch (parse_error &e) { @@ -232,8 +230,7 @@ namespace net } // Send the response - send_packet( - true, control::Opcode::PING_RSP, ping); + send_packet(true, control::Opcode::PING_RSP, ping); } catch (parse_error &e) { From 38e0547e8413abffbc118c054c983a5b05717dc0 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 13 Apr 2018 03:43:03 +0100 Subject: [PATCH 36/49] protocol: Rename control messages to SESSION_OFFER and SESSION_ACCEPT I like these names better --- include/ki/protocol/control/Opcode.h | 4 +- .../{ServerHello.h => SessionAccept.h} | 6 +-- .../control/{ClientHello.h => SessionOffer.h} | 6 +-- src/protocol/CMakeLists.txt | 4 +- .../{ClientHello.cpp => SessionAccept.cpp} | 24 ++++++------ .../{ServerHello.cpp => SessionOffer.cpp} | 26 ++++++------- src/protocol/net/Session.cpp | 38 +++++++++---------- 7 files changed, 54 insertions(+), 54 deletions(-) rename include/ki/protocol/control/{ServerHello.h => SessionAccept.h} (84%) rename include/ki/protocol/control/{ClientHello.h => SessionOffer.h} (84%) rename src/protocol/control/{ClientHello.cpp => SessionAccept.cpp} (70%) rename src/protocol/control/{ServerHello.cpp => SessionOffer.cpp} (66%) diff --git a/include/ki/protocol/control/Opcode.h b/include/ki/protocol/control/Opcode.h index 85ee88e..ab6b489 100644 --- a/include/ki/protocol/control/Opcode.h +++ b/include/ki/protocol/control/Opcode.h @@ -10,11 +10,11 @@ namespace control enum class Opcode : uint8_t { NONE = 0, - SERVER_HELLO = 0, + SESSION_OFFER = 0, UDP_HELLO = 1, PING = 3, PING_RSP = 4, - CLIENT_HELLO = 5 + SESSION_ACCEPT = 5 }; } } diff --git a/include/ki/protocol/control/ServerHello.h b/include/ki/protocol/control/SessionAccept.h similarity index 84% rename from include/ki/protocol/control/ServerHello.h rename to include/ki/protocol/control/SessionAccept.h index dd76843..4350e11 100644 --- a/include/ki/protocol/control/ServerHello.h +++ b/include/ki/protocol/control/SessionAccept.h @@ -9,12 +9,12 @@ namespace protocol { namespace control { - class ServerHello final : public util::Serializable + class SessionAccept final : public util::Serializable { public: - ServerHello(uint16_t session_id = 0, + SessionAccept(uint16_t session_id = 0, int32_t timestamp = 0, uint32_t milliseconds = 0); - virtual ~ServerHello() = default; + virtual ~SessionAccept() = default; uint16_t get_session_id() const; void set_session_id(uint16_t session_id); diff --git a/include/ki/protocol/control/ClientHello.h b/include/ki/protocol/control/SessionOffer.h similarity index 84% rename from include/ki/protocol/control/ClientHello.h rename to include/ki/protocol/control/SessionOffer.h index 98900a6..ab59fb1 100644 --- a/include/ki/protocol/control/ClientHello.h +++ b/include/ki/protocol/control/SessionOffer.h @@ -9,12 +9,12 @@ namespace protocol { namespace control { - class ClientHello final : public util::Serializable + class SessionOffer final : public util::Serializable { public: - ClientHello(uint16_t session_id = 0, + SessionOffer(uint16_t session_id = 0, int32_t timestamp = 0, uint32_t milliseconds = 0); - virtual ~ClientHello() = default; + virtual ~SessionOffer() = default; uint16_t get_session_id() const; void set_session_id(uint16_t session_id); diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt index c3e5e38..849f3fe 100644 --- a/src/protocol/CMakeLists.txt +++ b/src/protocol/CMakeLists.txt @@ -1,8 +1,8 @@ target_sources(${PROJECT_NAME} PRIVATE - ${PROJECT_SOURCE_DIR}/src/protocol/control/ClientHello.cpp - ${PROJECT_SOURCE_DIR}/src/protocol/control/ServerHello.cpp ${PROJECT_SOURCE_DIR}/src/protocol/control/Ping.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/control/SessionAccept.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/control/SessionOffer.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/Message.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageBuilder.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageManager.cpp diff --git a/src/protocol/control/ClientHello.cpp b/src/protocol/control/SessionAccept.cpp similarity index 70% rename from src/protocol/control/ClientHello.cpp rename to src/protocol/control/SessionAccept.cpp index 007ec21..26bd526 100644 --- a/src/protocol/control/ClientHello.cpp +++ b/src/protocol/control/SessionAccept.cpp @@ -1,4 +1,4 @@ -#include "ki/protocol/control/ClientHello.h" +#include "ki/protocol/control/SessionAccept.h" #include "ki/dml/Record.h" #include "ki/protocol/exception.h" @@ -8,7 +8,7 @@ namespace protocol { namespace control { - ClientHello::ClientHello(const uint16_t session_id, + SessionAccept::SessionAccept(const uint16_t session_id, const int32_t timestamp, const uint32_t milliseconds) { m_session_id = session_id; @@ -16,37 +16,37 @@ namespace control m_milliseconds = milliseconds; } - uint16_t ClientHello::get_session_id() const + uint16_t SessionAccept::get_session_id() const { return m_session_id; } - void ClientHello::set_session_id(const uint16_t session_id) + void SessionAccept::set_session_id(const uint16_t session_id) { m_session_id = session_id; } - int32_t ClientHello::get_timestamp() const + int32_t SessionAccept::get_timestamp() const { return m_timestamp; } - void ClientHello::set_timestamp(const int32_t timestamp) + void SessionAccept::set_timestamp(const int32_t timestamp) { m_timestamp = timestamp; } - uint32_t ClientHello::get_milliseconds() const + uint32_t SessionAccept::get_milliseconds() const { return m_milliseconds; } - void ClientHello::set_milliseconds(const uint32_t milliseconds) + void SessionAccept::set_milliseconds(const uint32_t milliseconds) { m_milliseconds = milliseconds; } - void ClientHello::write_to(std::ostream& ostream) const + void SessionAccept::write_to(std::ostream& ostream) const { dml::Record record; record.add_field("unknown"); @@ -57,7 +57,7 @@ namespace control record.write_to(ostream); } - void ClientHello::read_from(std::istream& istream) + void SessionAccept::read_from(std::istream& istream) { dml::Record record; record.add_field("unknown"); @@ -72,7 +72,7 @@ namespace control catch (dml::parse_error &e) { std::ostringstream oss; - oss << "Error reading ClientHello payload: " << e.what(); + oss << "Error reading SessionAccept payload: " << e.what(); throw parse_error(oss.str()); } @@ -81,7 +81,7 @@ namespace control m_session_id = session_id->get_value(); } - size_t ClientHello::get_size() const + size_t SessionAccept::get_size() const { return sizeof(dml::USHRT) + sizeof(dml::UINT) + sizeof(dml::INT) + sizeof(dml::UINT) + diff --git a/src/protocol/control/ServerHello.cpp b/src/protocol/control/SessionOffer.cpp similarity index 66% rename from src/protocol/control/ServerHello.cpp rename to src/protocol/control/SessionOffer.cpp index 450a5b5..4ca2635 100644 --- a/src/protocol/control/ServerHello.cpp +++ b/src/protocol/control/SessionOffer.cpp @@ -1,4 +1,4 @@ -#include "ki/protocol/control/ServerHello.h" +#include "ki/protocol/control/SessionOffer.h" #include "ki/dml/Record.h" #include "ki/protocol/exception.h" @@ -8,7 +8,7 @@ namespace protocol { namespace control { - ServerHello::ServerHello(const uint16_t session_id, + SessionOffer::SessionOffer(const uint16_t session_id, const int32_t timestamp, const uint32_t milliseconds) { m_session_id = session_id; @@ -16,37 +16,37 @@ namespace control m_milliseconds = milliseconds; } - uint16_t ServerHello::get_session_id() const + uint16_t SessionOffer::get_session_id() const { return m_session_id; } - void ServerHello::set_session_id(const uint16_t session_id) + void SessionOffer::set_session_id(const uint16_t session_id) { m_session_id = session_id; } - int32_t ServerHello::get_timestamp() const + int32_t SessionOffer::get_timestamp() const { return m_timestamp; } - void ServerHello::set_timestamp(const int32_t timestamp) + void SessionOffer::set_timestamp(const int32_t timestamp) { m_timestamp = timestamp; } - uint32_t ServerHello::get_milliseconds() const + uint32_t SessionOffer::get_milliseconds() const { return m_milliseconds; } - void ServerHello::set_milliseconds(const uint32_t milliseconds) + void SessionOffer::set_milliseconds(const uint32_t milliseconds) { m_milliseconds = milliseconds; } - void ServerHello::write_to(std::ostream& ostream) const + void SessionOffer::write_to(std::ostream& ostream) const { dml::Record record; record.add_field("m_session_id")->set_value(m_session_id); @@ -56,7 +56,7 @@ namespace control record.write_to(ostream); } - void ServerHello::read_from(std::istream& istream) + void SessionOffer::read_from(std::istream& istream) { dml::Record record; auto *session_id = record.add_field("m_session_id"); @@ -70,7 +70,7 @@ namespace control catch (dml::parse_error &e) { std::ostringstream oss; - oss << "Error reading ServerHello payload: " << e.what(); + oss << "Error reading SessionOffer payload: " << e.what(); throw parse_error(oss.str()); } @@ -79,9 +79,9 @@ namespace control m_milliseconds = milliseconds->get_value(); } - size_t ServerHello::get_size() const + size_t SessionOffer::get_size() const { - return sizeof(dml::USHRT) + sizeof(dml::UINT) + + return sizeof(dml::USHRT) + sizeof(dml::UINT) + sizeof(dml::INT) + sizeof(dml::UINT); } } diff --git a/src/protocol/net/Session.cpp b/src/protocol/net/Session.cpp index 1cfa814..30bbdf7 100644 --- a/src/protocol/net/Session.cpp +++ b/src/protocol/net/Session.cpp @@ -1,7 +1,7 @@ #include "ki/protocol/net/Session.h" #include "ki/protocol/exception.h" -#include "ki/protocol/control/ServerHello.h" -#include "ki/protocol/control/ClientHello.h" +#include "ki/protocol/control/SessionOffer.h" +#include "ki/protocol/control/SessionAccept.h" #include "ki/protocol/control/Ping.h" namespace ki @@ -75,7 +75,7 @@ namespace net void Session::on_connected() { // If this is the server-side of a Session - // we need to send SERVER_HELLO first. + // we need to send SESSION_OFFER first. if (get_type() == ParticipantType::SERVER) { // Work out the current timestamp and how many milliseconds @@ -88,9 +88,9 @@ namespace net now.time_since_epoch() ).count() - (timestamp * 1000); - // Send a SERVER_HELLO packet to the client - const control::ServerHello hello(m_id, timestamp, milliseconds); - send_packet(true, control::Opcode::SERVER_HELLO, hello); + // Send a SESSION_OFFER packet to the client + const control::SessionOffer hello(m_id, timestamp, milliseconds); + send_packet(true, control::Opcode::SESSION_OFFER, hello); } } @@ -122,11 +122,11 @@ namespace net { switch ((control::Opcode)header.get_opcode()) { - case (control::Opcode::SERVER_HELLO): + case (control::Opcode::SESSION_OFFER): on_server_hello(); break; - case (control::Opcode::CLIENT_HELLO): + case (control::Opcode::SESSION_ACCEPT): on_client_hello(); break; @@ -146,7 +146,7 @@ namespace net void Session::on_server_hello() { // If this is the server-side of a Session - // we can't handle a SERVER_HELLO + // we can't handle a SESSION_OFFER if (get_type() != ParticipantType::CLIENT) { close(); @@ -157,7 +157,7 @@ namespace net try { // We've been given our id from the server now - const auto server_hello = read_data(); + const auto server_hello = read_data(); m_id = server_hello.get_session_id(); on_hello(m_id, server_hello.get_timestamp(), @@ -173,13 +173,13 @@ namespace net now.time_since_epoch() ).count() - (timestamp * 1000); - // Send a CLIENT_HELLO packet to the server - const control::ClientHello hello(m_id, timestamp, milliseconds); - send_packet(true, control::Opcode::CLIENT_HELLO, hello); + // Send a SESSION_ACCEPT packet to the server + const control::SessionAccept hello(m_id, timestamp, milliseconds); + send_packet(true, control::Opcode::SESSION_ACCEPT, hello); } catch (parse_error &e) { - // The CLIENT_HELLO wasn't valid... + // The SESSION_ACCEPT wasn't valid... // Close the session close(); } @@ -188,7 +188,7 @@ namespace net void Session::on_client_hello() { // If this is the client-side of a Session - // we can't handle a CLIENT_HELLO + // we can't handle a SESSION_ACCEPT if (get_type() != ParticipantType::SERVER) { close(); @@ -199,14 +199,14 @@ namespace net try { // The session is now established! - const auto client_hello = read_data(); + const auto client_hello = read_data(); on_hello(client_hello.get_session_id(), client_hello.get_timestamp(), client_hello.get_milliseconds()); } catch (parse_error &e) { - // The CLIENT_HELLO wasn't valid... + // The SESSION_ACCEPT wasn't valid... // Close the session close(); } @@ -234,7 +234,7 @@ namespace net } catch (parse_error &e) { - // The CLIENT_HELLO wasn't valid... + // The SESSION_ACCEPT wasn't valid... // Close the session close(); } @@ -249,7 +249,7 @@ namespace net } catch (parse_error &e) { - // The CLIENT_HELLO wasn't valid... + // The SESSION_ACCEPT wasn't valid... // Close the session close(); } From bdededbb84c2a5567502773f33f041bb2374b097 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Sun, 15 Apr 2018 22:52:13 +0100 Subject: [PATCH 37/49] protocol: Combine Participant and Session Also removes ParticipantType --- .../control/{Ping.h => ClientKeepAlive.h} | 14 +- include/ki/protocol/control/Opcode.h | 4 +- include/ki/protocol/control/ServerKeepAlive.h | 28 ++ include/ki/protocol/net/ClientDMLSession.h | 35 +++ include/ki/protocol/net/ClientSession.h | 34 +++ include/ki/protocol/net/DMLSession.h | 13 +- include/ki/protocol/net/ServerDMLSession.h | 35 +++ include/ki/protocol/net/ServerSession.h | 34 +++ include/ki/protocol/net/Session.h | 75 ----- src/protocol/CMakeLists.txt | 10 +- .../control/{Ping.cpp => ClientKeepAlive.cpp} | 31 +- src/protocol/control/ServerKeepAlive.cpp | 61 ++++ src/protocol/net/ClientSession.cpp | 173 +++++++++++ src/protocol/net/DMLSession.cpp | 7 +- src/protocol/net/ServerSession.cpp | 171 +++++++++++ src/protocol/net/Session.cpp | 284 ------------------ 16 files changed, 613 insertions(+), 396 deletions(-) rename include/ki/protocol/control/{Ping.h => ClientKeepAlive.h} (65%) create mode 100644 include/ki/protocol/control/ServerKeepAlive.h create mode 100644 include/ki/protocol/net/ClientDMLSession.h create mode 100644 include/ki/protocol/net/ClientSession.h create mode 100644 include/ki/protocol/net/ServerDMLSession.h create mode 100644 include/ki/protocol/net/ServerSession.h delete mode 100644 include/ki/protocol/net/Session.h rename src/protocol/control/{Ping.cpp => ClientKeepAlive.cpp} (55%) create mode 100644 src/protocol/control/ServerKeepAlive.cpp create mode 100644 src/protocol/net/ClientSession.cpp create mode 100644 src/protocol/net/ServerSession.cpp delete mode 100644 src/protocol/net/Session.cpp diff --git a/include/ki/protocol/control/Ping.h b/include/ki/protocol/control/ClientKeepAlive.h similarity index 65% rename from include/ki/protocol/control/Ping.h rename to include/ki/protocol/control/ClientKeepAlive.h index 1b102c0..802a1bd 100644 --- a/include/ki/protocol/control/Ping.h +++ b/include/ki/protocol/control/ClientKeepAlive.h @@ -8,12 +8,12 @@ namespace protocol { namespace control { - class Ping final : public util::Serializable + class ClientKeepAlive final : public util::Serializable { public: - Ping(uint16_t session_id = 0, - uint16_t milliseconds = 0, uint8_t minutes = 0); - virtual ~Ping() = default; + ClientKeepAlive(uint16_t session_id = 0, + uint16_t milliseconds = 0, uint16_t minutes = 0); + virtual ~ClientKeepAlive() = default; uint16_t get_session_id() const; void set_session_id(uint16_t session_id); @@ -21,8 +21,8 @@ namespace control uint16_t get_milliseconds() const; void set_milliseconds(uint16_t milliseconds); - uint8_t get_minutes() const; - void set_minutes(uint8_t minutes); + uint16_t get_minutes() const; + void set_minutes(uint16_t minutes); void write_to(std::ostream &ostream) const override final; void read_from(std::istream &istream) override final; @@ -30,7 +30,7 @@ namespace control private: uint16_t m_session_id; uint16_t m_milliseconds; - uint8_t m_minutes; + uint16_t m_minutes; }; } } diff --git a/include/ki/protocol/control/Opcode.h b/include/ki/protocol/control/Opcode.h index ab6b489..c63b6aa 100644 --- a/include/ki/protocol/control/Opcode.h +++ b/include/ki/protocol/control/Opcode.h @@ -12,8 +12,8 @@ namespace control NONE = 0, SESSION_OFFER = 0, UDP_HELLO = 1, - PING = 3, - PING_RSP = 4, + KEEP_ALIVE = 3, + KEEP_ALIVE_RSP = 4, SESSION_ACCEPT = 5 }; } diff --git a/include/ki/protocol/control/ServerKeepAlive.h b/include/ki/protocol/control/ServerKeepAlive.h new file mode 100644 index 0000000..c7ad516 --- /dev/null +++ b/include/ki/protocol/control/ServerKeepAlive.h @@ -0,0 +1,28 @@ +#pragma once +#include "../../util/Serializable.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace control +{ + class ServerKeepAlive final : public util::Serializable + { + public: + ServerKeepAlive(uint32_t timestamp = 0); + virtual ~ServerKeepAlive() = default; + + uint32_t get_timestamp() const; + void set_timestamp(uint32_t timestamp); + + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; + private: + uint32_t m_timestamp; + }; +} +} +} diff --git a/include/ki/protocol/net/ClientDMLSession.h b/include/ki/protocol/net/ClientDMLSession.h new file mode 100644 index 0000000..a0ced83 --- /dev/null +++ b/include/ki/protocol/net/ClientDMLSession.h @@ -0,0 +1,35 @@ +#pragma once +#include "ClientSession.h" +#include "DMLSession.h" + +// Disable inheritance via dominance warning +#if _MSC_VER +#pragma warning(disable: 4250) +#endif + +namespace ki +{ +namespace protocol +{ +namespace net +{ + class ClientDMLSession : public ClientSession, public DMLSession + { + // Explicitly specify that we are intentionally inheritting + // via dominance. + using DMLSession::on_application_message; + using ClientSession::on_control_message; + using ClientSession::is_alive; + public: + ClientDMLSession(const uint16_t id, const dml::MessageManager &manager) + : Session(id), ClientSession(id), DMLSession(id, manager) {} + virtual ~ClientDMLSession() = default; + }; +} +} +} + +// Re-enable inheritance via dominance warning +#if _MSC_VER +#pragma warning(default: 4250) +#endif diff --git a/include/ki/protocol/net/ClientSession.h b/include/ki/protocol/net/ClientSession.h new file mode 100644 index 0000000..a9b8351 --- /dev/null +++ b/include/ki/protocol/net/ClientSession.h @@ -0,0 +1,34 @@ +#pragma once +#include "Session.h" + +#define KI_SERVER_HEARTBEAT 60 + +namespace ki +{ +namespace protocol +{ +namespace net +{ + /** + * Implements client-sided session logic. + */ + class ClientSession : public virtual Session + { + public: + explicit ClientSession(uint16_t id); + virtual ~ClientSession() = default; + + void send_keep_alive(); + bool is_alive() const override; + protected: + void on_connected(); + virtual void on_established() {} + void on_control_message(const PacketHeader& header) override; + private: + void on_session_offer(); + void on_keep_alive(); + void on_keep_alive_response(); + }; +} +} +} \ No newline at end of file diff --git a/include/ki/protocol/net/DMLSession.h b/include/ki/protocol/net/DMLSession.h index 3b0c66e..b5a6a32 100644 --- a/include/ki/protocol/net/DMLSession.h +++ b/include/ki/protocol/net/DMLSession.h @@ -8,12 +8,15 @@ namespace protocol { namespace net { - class DMLSession : public Session + /** + * Implements an application protocol that uses the DML + * message system (as seen in Wizard101 and Pirate101). + */ + class DMLSession : public virtual Session { public: - DMLSession(ParticipantType type, uint16_t id, - const dml::MessageManager &manager); - ~DMLSession() = default; + DMLSession(uint16_t id, const dml::MessageManager &manager); + virtual ~DMLSession() = default; void send_message(const dml::Message &message); protected: @@ -25,4 +28,4 @@ namespace net }; } } -} \ No newline at end of file +} diff --git a/include/ki/protocol/net/ServerDMLSession.h b/include/ki/protocol/net/ServerDMLSession.h new file mode 100644 index 0000000..d450626 --- /dev/null +++ b/include/ki/protocol/net/ServerDMLSession.h @@ -0,0 +1,35 @@ +#pragma once +#include "ServerSession.h" +#include "DMLSession.h" + +// Disable inheritance via dominance warning +#if _MSC_VER +#pragma warning(disable: 4250) +#endif + +namespace ki +{ +namespace protocol +{ +namespace net +{ + class ServerDMLSession : public ServerSession, public DMLSession + { + // Explicitly specify that we are intentionally inheritting + // via dominance. + using DMLSession::on_application_message; + using ServerSession::on_control_message; + using ServerSession::is_alive; + public: + ServerDMLSession(const uint16_t id, const dml::MessageManager &manager) + : Session(id), ServerSession(id), DMLSession(id, manager) {} + virtual ~ServerDMLSession() = default; + }; +} +} +} + +// Re-enable inheritance via dominance warning +#if _MSC_VER +#pragma warning(default: 4250) +#endif diff --git a/include/ki/protocol/net/ServerSession.h b/include/ki/protocol/net/ServerSession.h new file mode 100644 index 0000000..77b4da7 --- /dev/null +++ b/include/ki/protocol/net/ServerSession.h @@ -0,0 +1,34 @@ +#pragma once +#include "Session.h" + +#define KI_CLIENT_HEARTBEAT 10 + +namespace ki +{ +namespace protocol +{ +namespace net +{ + /** + * Implements server-sided session logic. + */ + class ServerSession : public virtual Session + { + public: + explicit ServerSession(uint16_t id); + virtual ~ServerSession() = default; + + void send_keep_alive(uint32_t milliseconds_since_startup); + bool is_alive() const override; + protected: + void on_connected(); + virtual void on_established() {} + void on_control_message(const PacketHeader& header) override; + private: + void on_session_accept(); + void on_keep_alive(); + void on_keep_alive_response(); + }; +} +} +} \ No newline at end of file diff --git a/include/ki/protocol/net/Session.h b/include/ki/protocol/net/Session.h deleted file mode 100644 index fe97b1d..0000000 --- a/include/ki/protocol/net/Session.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -#include "Participant.h" -#include "PacketHeader.h" -#include "ki/protocol/control/Opcode.h" -#include "../../util/Serializable.h" -#include -#include -#include - -namespace ki -{ -namespace protocol -{ -namespace net -{ - /** - * This class implements session logic on top of the - * low-level Participant class. - */ - class Session : public Participant - { - public: - Session(ParticipantType type, uint16_t id); - - uint16_t get_id() const; - bool is_established() const; - - uint8_t get_access_level() const; - void set_access_level(uint8_t access_level); - - uint16_t get_latency() const; - - bool is_alive() const; - - void send_packet(bool is_control, control::Opcode opcode, - const util::Serializable &data); - protected: - template - DataT read_data() - { - static_assert(std::is_base_of::value, - "DataT must inherit Serializable."); - - DataT data = DataT(); - data.read_from(m_data_stream); - return data; - } - - void on_connected(); - virtual void on_established() {}; - virtual void on_application_message(const PacketHeader &header) {}; - virtual void on_invalid_packet() {}; - private: - uint16_t m_id; - bool m_established; - uint8_t m_access_level; - uint16_t m_latency; - - std::chrono::steady_clock::time_point m_creation_time; - std::chrono::steady_clock::time_point m_establish_time; - std::chrono::steady_clock::time_point m_last_heartbeat; - - void on_packet_available() override final; - void on_control_message(const PacketHeader &header); - void on_server_hello(); - void on_client_hello(); - void on_ping(); - void on_ping_response(); - - void on_hello(uint16_t session_id, uint32_t timestamp, - uint16_t milliseconds); - }; -} -} -} diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt index 849f3fe..79c513c 100644 --- a/src/protocol/CMakeLists.txt +++ b/src/protocol/CMakeLists.txt @@ -1,6 +1,7 @@ target_sources(${PROJECT_NAME} PRIVATE - ${PROJECT_SOURCE_DIR}/src/protocol/control/Ping.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/control/ClientKeepAlive.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/control/ServerKeepAlive.cpp ${PROJECT_SOURCE_DIR}/src/protocol/control/SessionAccept.cpp ${PROJECT_SOURCE_DIR}/src/protocol/control/SessionOffer.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/Message.cpp @@ -8,8 +9,9 @@ target_sources(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageManager.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageModule.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageTemplate.cpp - ${PROJECT_SOURCE_DIR}/src/protocol/net/PacketHeader.cpp - ${PROJECT_SOURCE_DIR}/src/protocol/net/Participant.cpp - ${PROJECT_SOURCE_DIR}/src/protocol/net/Session.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/net/ClientSession.cpp ${PROJECT_SOURCE_DIR}/src/protocol/net/DMLSession.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/net/PacketHeader.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/net/ServerSession.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/net/Session.cpp ) \ No newline at end of file diff --git a/src/protocol/control/Ping.cpp b/src/protocol/control/ClientKeepAlive.cpp similarity index 55% rename from src/protocol/control/Ping.cpp rename to src/protocol/control/ClientKeepAlive.cpp index 5e7987b..0077fec 100644 --- a/src/protocol/control/Ping.cpp +++ b/src/protocol/control/ClientKeepAlive.cpp @@ -1,4 +1,4 @@ -#include "ki/protocol/control/Ping.h" +#include "ki/protocol/control/ClientKeepAlive.h" #include "ki/dml/Record.h" #include "ki/protocol/exception.h" @@ -8,58 +8,59 @@ namespace protocol { namespace control { - Ping::Ping(uint16_t session_id, uint16_t milliseconds, uint8_t minutes) + ClientKeepAlive::ClientKeepAlive(const uint16_t session_id, const uint16_t milliseconds, + const uint16_t minutes) { m_session_id = session_id; m_milliseconds = milliseconds; m_minutes = minutes; } - uint16_t Ping::get_session_id() const + uint16_t ClientKeepAlive::get_session_id() const { return m_session_id; } - void Ping::set_session_id(uint16_t session_id) + void ClientKeepAlive::set_session_id(const uint16_t session_id) { m_session_id = session_id; } - uint16_t Ping::get_milliseconds() const + uint16_t ClientKeepAlive::get_milliseconds() const { return m_milliseconds; } - void Ping::set_milliseconds(uint16_t milliseconds) + void ClientKeepAlive::set_milliseconds(const uint16_t milliseconds) { m_milliseconds = milliseconds; } - uint8_t Ping::get_minutes() const + uint16_t ClientKeepAlive::get_minutes() const { return m_minutes; } - void Ping::set_minutes(uint8_t minutes) + void ClientKeepAlive::set_minutes(const uint16_t minutes) { m_minutes = minutes; } - void Ping::write_to(std::ostream &ostream) const + void ClientKeepAlive::write_to(std::ostream &ostream) const { dml::Record record; record.add_field("m_session_id")->set_value(m_session_id); record.add_field("m_milliseconds")->set_value(m_milliseconds); - record.add_field("m_minutes")->set_value(m_minutes); + record.add_field("m_minutes")->set_value(m_minutes); record.write_to(ostream); } - void Ping::read_from(std::istream &istream) + void ClientKeepAlive::read_from(std::istream &istream) { dml::Record record; auto *session_id = record.add_field("m_session_id"); auto *milliseconds = record.add_field("m_milliseconds"); - auto *minutes = record.add_field("m_minutes"); + auto *minutes = record.add_field("m_minutes"); try { record.read_from(istream); @@ -67,7 +68,7 @@ namespace control catch (dml::parse_error &e) { std::ostringstream oss; - oss << "Error reading Ping payload: " << e.what(); + oss << "Error reading ClientKeepAlive payload: " << e.what(); throw parse_error(oss.str()); } @@ -76,10 +77,10 @@ namespace control m_minutes = minutes->get_value(); } - size_t Ping::get_size() const + size_t ClientKeepAlive::get_size() const { return sizeof(dml::USHRT) + sizeof(dml::USHRT) + - sizeof(dml::UBYT); + sizeof(dml::USHRT); } } } diff --git a/src/protocol/control/ServerKeepAlive.cpp b/src/protocol/control/ServerKeepAlive.cpp new file mode 100644 index 0000000..c967921 --- /dev/null +++ b/src/protocol/control/ServerKeepAlive.cpp @@ -0,0 +1,61 @@ +#include "ki/protocol/control/ServerKeepAlive.h" +#include "ki/dml/Record.h" +#include "ki/protocol/exception.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace control +{ + ServerKeepAlive::ServerKeepAlive(const uint32_t timestamp) + { + m_timestamp = timestamp; + } + + uint32_t ServerKeepAlive::get_timestamp() const + { + return m_timestamp; + } + + void ServerKeepAlive::set_timestamp(const uint32_t timestamp) + { + m_timestamp = timestamp; + } + + void ServerKeepAlive::write_to(std::ostream& ostream) const + { + dml::Record record; + record.add_field("m_session_id"); + record.add_field("m_timestamp")->set_value(m_timestamp); + record.write_to(ostream); + } + + void ServerKeepAlive::read_from(std::istream& istream) + { + dml::Record record; + record.add_field("m_session_id"); + auto *timestamp = record.add_field("m_timestamp"); + try + { + record.read_from(istream); + } + catch (dml::parse_error &e) + { + std::ostringstream oss; + oss << "Error reading ServerKeepAlive payload: " << e.what(); + throw parse_error(oss.str()); + } + + m_timestamp = timestamp->get_value(); + } + + size_t ServerKeepAlive::get_size() const + { + return sizeof(dml::USHRT) + sizeof(dml::INT); + } + +} +} +} diff --git a/src/protocol/net/ClientSession.cpp b/src/protocol/net/ClientSession.cpp new file mode 100644 index 0000000..db70956 --- /dev/null +++ b/src/protocol/net/ClientSession.cpp @@ -0,0 +1,173 @@ +#include "ki/protocol/net/ClientSession.h" +#include "ki/protocol/control/SessionOffer.h" +#include "ki/protocol/control/SessionAccept.h" +#include "ki/protocol/control/ClientKeepAlive.h" +#include "ki/protocol/control/ServerKeepAlive.h" +#include "ki/protocol/exception.h" + +namespace ki +{ +namespace protocol +{ +namespace net +{ + ClientSession::ClientSession(const uint16_t id) + : Session(id) {} + + void ClientSession::send_keep_alive() + { + // Don't send a keep alive if we're waiting for a response + if (m_waiting_for_keep_alive_response) + return; + m_waiting_for_keep_alive_response = true; + + // Work out how many minutes have been since the establish time, and + // how many milliseconds we are in to the current minute. + const auto time_since_establish = std::chrono::steady_clock::now() - m_establish_time; + const auto minutes = std::chrono::duration_cast(time_since_establish); + const auto milliseconds = std::chrono::duration_cast( + time_since_establish - minutes + ).count(); + + // Send a KEEP_ALIVE packet + control::ClientKeepAlive keep_alive(m_id, milliseconds, minutes.count()); + send_packet(true, (uint8_t)control::Opcode::KEEP_ALIVE, keep_alive); + m_last_sent_heartbeat_time = std::chrono::steady_clock::now(); + } + + bool ClientSession::is_alive() const + { + // If the session isn't established yet, use the time of + // creation to decide whether this session is alive. + if (!m_established) + return std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_creation_time + ).count() <= KI_CONNECTION_TIMEOUT; + + // Otherwise, use the last time we received a heartbeat. + return std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_last_received_heartbeat_time + ).count() <= KI_SERVER_HEARTBEAT; + } + + void ClientSession::on_connected() + { + m_connection_time = std::chrono::steady_clock::now(); + } + + void ClientSession::on_control_message(const PacketHeader& header) + { + switch ((control::Opcode)header.get_opcode()) + { + case control::Opcode::SESSION_OFFER: + on_session_offer(); + break; + + case control::Opcode::KEEP_ALIVE: + on_keep_alive(); + break; + + case control::Opcode::KEEP_ALIVE_RSP: + on_keep_alive_response(); + break; + + default: + close(); + break; + } + } + + void ClientSession::on_session_offer() + { + // Read the payload data into a structure + control::SessionOffer offer; + try + { + offer = read_data(); + } + catch (parse_error &e) + { + // The SESSION_ACCEPT wasn't valid... + // Close the session + close(); + return; + } + + // Should this session have already timed out? + if (std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_connection_time + ).count() > KI_CONNECTION_TIMEOUT) + { + close(); + return; + } + + // Work out the current timestamp and how many milliseconds + // have elapsed in the current second. + auto now = std::chrono::system_clock::now(); + const auto timestamp = std::chrono::duration_cast( + now.time_since_epoch() + ).count(); + const auto milliseconds = std::chrono::duration_cast( + now.time_since_epoch() + ).count() - (timestamp * 1000); + + // Accept the session + m_id = offer.get_session_id(); + control::SessionAccept accept(m_id, timestamp, milliseconds); + send_packet(true, (uint8_t)control::Opcode::SESSION_ACCEPT, accept); + + // The session is successfully established + m_established = true; + m_establish_time = std::chrono::steady_clock::now(); + m_last_received_heartbeat_time = m_establish_time; + on_established(); + } + + void ClientSession::on_keep_alive() + { + // Read the payload data into a structure + control::ServerKeepAlive keep_alive; + try + { + keep_alive = read_data(); + } + catch (parse_error &e) + { + // The KEEP_ALIVE wasn't valid... + // Close the session + close(); + return; + } + + // Send the response + m_last_received_heartbeat_time = std::chrono::steady_clock::now(); + send_packet(true, (uint8_t)control::Opcode::KEEP_ALIVE_RSP, keep_alive); + } + + void ClientSession::on_keep_alive_response() + { + // Read the payload data into a structure + try + { + // We don't actually need the data inside, but + // read it to check if the structure is right. + read_data(); + } + catch (parse_error &e) + { + // The KEEP_ALIVE_RSP wasn't valid... + // Close the session + close(); + return; + } + + // Calculate latency and allow for KEEP_ALIVE packets to be sent again + m_latency = std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_last_sent_heartbeat_time + ).count(); + m_waiting_for_keep_alive_response = false; + } +} +} +} diff --git a/src/protocol/net/DMLSession.cpp b/src/protocol/net/DMLSession.cpp index de524ed..a0e4b85 100644 --- a/src/protocol/net/DMLSession.cpp +++ b/src/protocol/net/DMLSession.cpp @@ -6,13 +6,12 @@ namespace protocol { namespace net { - DMLSession::DMLSession(const ParticipantType type, const uint16_t id, - const dml::MessageManager& manager) - : Session(type, id), m_manager(manager) {} + DMLSession::DMLSession(const uint16_t id, const dml::MessageManager& manager) + : Session(id), m_manager(manager) {} void DMLSession::send_message(const dml::Message& message) { - send_packet(false, control::Opcode::NONE, message); + send_packet(false, 0, message); } void DMLSession::on_application_message(const PacketHeader& header) diff --git a/src/protocol/net/ServerSession.cpp b/src/protocol/net/ServerSession.cpp new file mode 100644 index 0000000..5da2d09 --- /dev/null +++ b/src/protocol/net/ServerSession.cpp @@ -0,0 +1,171 @@ +#include "ki/protocol/net/ServerSession.h" +#include "ki/protocol/control/SessionOffer.h" +#include "ki/protocol/control/SessionAccept.h" +#include "ki/protocol/control/ClientKeepAlive.h" +#include "ki/protocol/control/ServerKeepAlive.h" +#include "ki/protocol/exception.h" + +namespace ki +{ +namespace protocol +{ +namespace net +{ + ServerSession::ServerSession(const uint16_t id) + : Session(id) {} + + void ServerSession::send_keep_alive(const uint32_t milliseconds_since_startup) + { + // Don't send a keep alive if we're waiting for a response + if (m_waiting_for_keep_alive_response) + return; + m_waiting_for_keep_alive_response = true; + + // Send a KEEP_ALIVE packet + const control::ServerKeepAlive keep_alive(milliseconds_since_startup); + send_packet(true, (uint8_t)control::Opcode::KEEP_ALIVE, keep_alive); + m_last_sent_heartbeat_time = std::chrono::steady_clock::now(); + } + + bool ServerSession::is_alive() const + { + // If the session isn't established yet, use the time of + // creation to decide whether this session is alive. + if (!m_established) + return std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_creation_time + ).count() <= KI_CONNECTION_TIMEOUT; + + // Otherwise, use the last time we received a heartbeat. + return std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_last_received_heartbeat_time + ).count() <= KI_CLIENT_HEARTBEAT; + } + + void ServerSession::on_connected() + { + m_connection_time = std::chrono::steady_clock::now(); + + // Work out the current timestamp and how many milliseconds + // have elapsed in the current second. + auto now = std::chrono::system_clock::now(); + const auto timestamp = std::chrono::duration_cast( + now.time_since_epoch() + ).count(); + const auto milliseconds = std::chrono::duration_cast( + now.time_since_epoch() + ).count() - (timestamp * 1000); + + // Send a SESSION_OFFER packet to the client + const control::SessionOffer offer(m_id, timestamp, milliseconds); + send_packet(true, (uint8_t)control::Opcode::SESSION_OFFER, offer); + } + + void ServerSession::on_control_message(const PacketHeader& header) + { + switch ((control::Opcode)header.get_opcode()) + { + case control::Opcode::SESSION_ACCEPT: + on_session_accept(); + break; + + case control::Opcode::KEEP_ALIVE: + on_keep_alive(); + break; + + case control::Opcode::KEEP_ALIVE_RSP: + on_keep_alive_response(); + break; + + default: + close(); + break; + } + } + + void ServerSession::on_session_accept() + { + // Read the payload data into a structure + control::SessionAccept accept; + try + { + accept = read_data(); + } + catch (parse_error &e) + { + // The SESSION_ACCEPT wasn't valid... + // Close the session + close(); + return; + } + + // Should this session have already timed out? + if (std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_connection_time + ).count() > KI_CONNECTION_TIMEOUT) + { + close(); + return; + } + + // Make sure they're accepting this session + if (accept.get_session_id() != m_id) + { + close(); + return; + } + + // The session is successfully established + m_established = true; + m_establish_time = std::chrono::steady_clock::now(); + m_last_received_heartbeat_time = m_establish_time; + on_established(); + } + + void ServerSession::on_keep_alive() + { + // Read the payload data into a structure + control::ClientKeepAlive keep_alive; + try + { + keep_alive = read_data(); + } + catch (parse_error &e) + { + // The KEEP_ALIVE wasn't valid... + // Close the session + close(); + return; + } + + // Send the response + m_last_received_heartbeat_time = std::chrono::steady_clock::now(); + send_packet(true, (uint8_t)control::Opcode::KEEP_ALIVE_RSP, keep_alive); + } + + void ServerSession::on_keep_alive_response() + { + // Read the payload data into a structure + try + { + // We don't actually need the data inside, but + // read it to check if the structure is right. + read_data(); + } + catch (parse_error &e) + { + // The KEEP_ALIVE_RSP wasn't valid... + // Close the session + close(); + return; + } + + // Calculate latency and allow for KEEP_ALIVE packets to be sent again + m_latency = std::chrono::duration_cast( + std::chrono::steady_clock::now() - m_last_sent_heartbeat_time + ).count(); + m_waiting_for_keep_alive_response = false; + } +} +} +} diff --git a/src/protocol/net/Session.cpp b/src/protocol/net/Session.cpp deleted file mode 100644 index 30bbdf7..0000000 --- a/src/protocol/net/Session.cpp +++ /dev/null @@ -1,284 +0,0 @@ -#include "ki/protocol/net/Session.h" -#include "ki/protocol/exception.h" -#include "ki/protocol/control/SessionOffer.h" -#include "ki/protocol/control/SessionAccept.h" -#include "ki/protocol/control/Ping.h" - -namespace ki -{ -namespace protocol -{ -namespace net -{ - Session::Session(const ParticipantType type, const uint16_t id) - : Participant(type) - { - m_id = id; - m_established = false; - m_access_level = 0; - m_latency = 0; - m_creation_time = std::chrono::steady_clock::now(); - } - - uint16_t Session::get_id() const - { - return m_id; - } - - bool Session::is_established() const - { - return m_established; - } - - uint8_t Session::get_access_level() const - { - return m_access_level; - } - - void Session::set_access_level(const uint8_t access_level) - { - m_access_level = access_level; - } - - uint16_t Session::get_latency() const - { - return m_latency; - } - - bool Session::is_alive() const - { - // If the session isn't established yet, use the time of - // creation to decide whether this session is alive. - if (!m_established) - return std::chrono::duration_cast( - std::chrono::steady_clock::now() - m_creation_time - ).count() <= 3; - - // Otherwise, use the last time we received a heartbeat. - return std::chrono::duration_cast( - std::chrono::steady_clock::now() - m_last_heartbeat - ).count() <= 10; - } - - void Session::send_packet(const bool is_control, const control::Opcode opcode, - const util::Serializable& data) - { - std::ostringstream ss; - PacketHeader header(is_control, (uint8_t)opcode); - header.write_to(ss); - data.write_to(ss); - - const auto buffer = ss.str(); - send_data(buffer.c_str(), buffer.length()); - } - - void Session::on_connected() - { - // If this is the server-side of a Session - // we need to send SESSION_OFFER first. - if (get_type() == ParticipantType::SERVER) - { - // Work out the current timestamp and how many milliseconds - // have elapsed in the current second. - auto now = std::chrono::system_clock::now(); - const auto timestamp = std::chrono::duration_cast( - now.time_since_epoch() - ).count(); - const auto milliseconds = std::chrono::duration_cast( - now.time_since_epoch() - ).count() - (timestamp * 1000); - - // Send a SESSION_OFFER packet to the client - const control::SessionOffer hello(m_id, timestamp, milliseconds); - send_packet(true, control::Opcode::SESSION_OFFER, hello); - } - } - - void Session::on_packet_available() - { - // Read the packet header - PacketHeader header; - try - { - header.read_from(m_data_stream); - } - catch (parse_error &e) - { - on_invalid_packet(); - return; - } - - // Hand off to the right handler based on - // whether this is a control packet or not - if (header.is_control()) - on_control_message(header); - else if (m_established) - on_application_message(header); - else - close(); - } - - void Session::on_control_message(const PacketHeader& header) - { - switch ((control::Opcode)header.get_opcode()) - { - case (control::Opcode::SESSION_OFFER): - on_server_hello(); - break; - - case (control::Opcode::SESSION_ACCEPT): - on_client_hello(); - break; - - case (control::Opcode::PING): - on_ping(); - break; - - case (control::Opcode::PING_RSP): - on_ping_response(); - break; - - default: - break; - } - } - - void Session::on_server_hello() - { - // If this is the server-side of a Session - // we can't handle a SESSION_OFFER - if (get_type() != ParticipantType::CLIENT) - { - close(); - return; - } - - // Read the payload data into a structure - try - { - // We've been given our id from the server now - const auto server_hello = read_data(); - m_id = server_hello.get_session_id(); - on_hello(m_id, - server_hello.get_timestamp(), - server_hello.get_milliseconds()); - - // Work out the current timestamp and how many milliseconds - // have elapsed in the current second. - auto now = std::chrono::system_clock::now(); - const auto timestamp = std::chrono::duration_cast( - now.time_since_epoch() - ).count(); - const auto milliseconds = std::chrono::duration_cast( - now.time_since_epoch() - ).count() - (timestamp * 1000); - - // Send a SESSION_ACCEPT packet to the server - const control::SessionAccept hello(m_id, timestamp, milliseconds); - send_packet(true, control::Opcode::SESSION_ACCEPT, hello); - } - catch (parse_error &e) - { - // The SESSION_ACCEPT wasn't valid... - // Close the session - close(); - } - } - - void Session::on_client_hello() - { - // If this is the client-side of a Session - // we can't handle a SESSION_ACCEPT - if (get_type() != ParticipantType::SERVER) - { - close(); - return; - } - - // Read the payload data into a structure - try - { - // The session is now established! - const auto client_hello = read_data(); - on_hello(client_hello.get_session_id(), - client_hello.get_timestamp(), - client_hello.get_milliseconds()); - } - catch (parse_error &e) - { - // The SESSION_ACCEPT wasn't valid... - // Close the session - close(); - } - } - - void Session::on_ping() - { - // Read the payload data into a structure - try - { - const auto ping = read_data(); - if (get_type() == ParticipantType::SERVER) - { - // Calculate latency - const auto send_time = m_establish_time + - std::chrono::milliseconds(ping.get_milliseconds()) + - std::chrono::minutes(ping.get_minutes()); - m_latency = std::chrono::duration_cast( - std::chrono::steady_clock::now() - send_time - ).count(); - } - - // Send the response - send_packet(true, control::Opcode::PING_RSP, ping); - } - catch (parse_error &e) - { - // The SESSION_ACCEPT wasn't valid... - // Close the session - close(); - } - } - - void Session::on_ping_response() - { - // Read the payload data into a structure - try - { - const auto ping = read_data(); - } - catch (parse_error &e) - { - // The SESSION_ACCEPT wasn't valid... - // Close the session - close(); - } - } - - void Session::on_hello(const uint16_t session_id, - const uint32_t timestamp, const uint16_t milliseconds) - { - // Make sure they're accepting this session - if (session_id != m_id) - { - close(); - return; - } - - // Calculate initial latency - const std::chrono::system_clock::time_point epoch; - const auto send_time = epoch + (std::chrono::seconds(timestamp) + - std::chrono::milliseconds(milliseconds)); - m_latency = std::chrono::duration_cast( - std::chrono::system_clock::now() - send_time - ).count(); - - // The session is successfully established - m_established = true; - m_establish_time = std::chrono::steady_clock::now(); - m_last_heartbeat = m_establish_time; - on_established(); - } -} -} -} From a58341b2792ea9de6d548f58a8e4cd1b15854600 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Sun, 15 Apr 2018 22:52:41 +0100 Subject: [PATCH 38/49] net: Forgot to push Session --- include/ki/protocol/net/Participant.h | 76 ----------- include/ki/protocol/net/Session.h | 122 ++++++++++++++++++ .../net/{Participant.cpp => Session.cpp} | 97 ++++++++++---- 3 files changed, 197 insertions(+), 98 deletions(-) delete mode 100644 include/ki/protocol/net/Participant.h create mode 100644 include/ki/protocol/net/Session.h rename src/protocol/net/{Participant.cpp => Session.cpp} (62%) diff --git a/include/ki/protocol/net/Participant.h b/include/ki/protocol/net/Participant.h deleted file mode 100644 index 67a11aa..0000000 --- a/include/ki/protocol/net/Participant.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once -#include -#include - -#define KI_DEFAULT_MAXIMUM_RECEIVE_SIZE 0x2000 -#define KI_START_SIGNAL 0xF00D - -namespace ki -{ -namespace protocol -{ -namespace net -{ - enum class ReceiveState - { - // Waiting for the 0xF00D start signal. - WAITING_FOR_START_SIGNAL, - - // Waiting for the 2-byte length. - WAITING_FOR_LENGTH, - - // Waiting for the packet data. - WAITING_FOR_PACKET - }; - - enum class ParticipantType - { - SERVER, - CLIENT - }; - - /** - * This class implements the packet framing logic when - * sending and receiving data to/from an external source. - */ - class Participant - { - public: - Participant(ParticipantType type); - virtual ~Participant() = default; - - ParticipantType get_type() const; - void set_type(ParticipantType type); - - uint16_t get_maximum_packet_size() const; - void set_maximum_packet_size(uint16_t maximum_packet_size); - protected: - std::stringstream m_data_stream; - - /** - * Frames raw data into a Packet, and transmits it. - */ - void send_data(const char *data, size_t size); - - /** - * Process incoming raw data into Packets. - * Once a packet is read into the internal data - * stream, handle_packet_available is called. - */ - void process_data(const char *data, size_t size); - - virtual void send_packet_data(const char *data, const size_t size) = 0; - virtual void on_packet_available() {}; - virtual void close() = 0; - private: - ParticipantType m_type; - uint16_t m_maximum_packet_size; - - ReceiveState m_receive_state; - uint16_t m_start_signal; - uint16_t m_incoming_packet_size; - uint8_t m_shift; - }; -} -} -} diff --git a/include/ki/protocol/net/Session.h b/include/ki/protocol/net/Session.h new file mode 100644 index 0000000..f3b7682 --- /dev/null +++ b/include/ki/protocol/net/Session.h @@ -0,0 +1,122 @@ +#pragma once +#include "PacketHeader.h" +#include "../control/Opcode.h" +#include "../../util/Serializable.h" +#include +#include +#include +#include + +#define KI_DEFAULT_MAXIMUM_RECEIVE_SIZE 0x2000 +#define KI_START_SIGNAL 0xF00D +#define KI_CONNECTION_TIMEOUT 3 + +namespace ki +{ +namespace protocol +{ +namespace net +{ + enum class ReceiveState + { + // Waiting for the 0xF00D start signal. + WAITING_FOR_START_SIGNAL, + + // Waiting for the 2-byte length. + WAITING_FOR_LENGTH, + + // Waiting for the packet data. + WAITING_FOR_PACKET + }; + + /** + * This class implements session and packet framing logic + * when sending and receiving data to/from an external + * source. + */ + class Session + { + public: + explicit Session(uint16_t id = 0); + virtual ~Session() = default; + + uint16_t get_maximum_packet_size() const; + void set_maximum_packet_size(uint16_t maximum_packet_size); + + uint16_t get_id() const; + bool is_established() const; + + uint8_t get_access_level() const; + void set_access_level(uint8_t access_level); + + uint16_t get_latency() const; + + virtual bool is_alive() const = 0; + + void send_packet(bool is_control, uint8_t opcode, + const util::Serializable &data); + protected: + /* Higher-level session members */ + uint16_t m_id; + bool m_established; + uint8_t m_access_level; + + /* Timing members */ + std::chrono::steady_clock::time_point m_creation_time; + std::chrono::steady_clock::time_point m_connection_time; + std::chrono::steady_clock::time_point m_establish_time; + std::chrono::steady_clock::time_point m_last_received_heartbeat_time; + std::chrono::steady_clock::time_point m_last_sent_heartbeat_time; + bool m_waiting_for_keep_alive_response; + uint16_t m_latency; + + // The packet data stream + std::stringstream m_data_stream; + + /** + * Reads a serializable structure from the data stream. + */ + template + DataT read_data() + { + static_assert(std::is_base_of::value, + "DataT must inherit Serializable."); + + DataT data = DataT(); + data.read_from(m_data_stream); + return data; + } + + /** + * Frames raw data into a Packet, and transmits it. + */ + void send_data(const char *data, size_t size); + + /** + * Process incoming raw data into Packets. + * Once a packet is read into the internal data + * stream, handle_packet_available is called. + */ + void process_data(const char *data, size_t size); + + /* Event handlers */ + virtual void on_invalid_packet() {} + virtual void on_control_message(const PacketHeader &header) {} + virtual void on_application_message(const PacketHeader &header) {} + + /* Low-level socket methods */ + virtual void send_packet_data(const char *data, const size_t size) = 0; + virtual void close() = 0; + private: + /* Low-level networking members */ + uint16_t m_maximum_packet_size; + ReceiveState m_receive_state; + uint16_t m_start_signal; + uint16_t m_incoming_packet_size; + uint8_t m_shift; + + void on_packet_available(); + }; +} +} +} diff --git a/src/protocol/net/Participant.cpp b/src/protocol/net/Session.cpp similarity index 62% rename from src/protocol/net/Participant.cpp rename to src/protocol/net/Session.cpp index 8e1fb73..cbfbbd3 100644 --- a/src/protocol/net/Participant.cpp +++ b/src/protocol/net/Session.cpp @@ -1,6 +1,5 @@ -#include "ki/protocol/net/Participant.h" +#include "ki/protocol/net/Session.h" #include "ki/protocol/exception.h" -#include namespace ki { @@ -8,43 +7,74 @@ namespace protocol { namespace net { - Participant::Participant(const ParticipantType type) - { - m_type = type; - m_maximum_packet_size = KI_DEFAULT_MAXIMUM_RECEIVE_SIZE; + Session::Session(const uint16_t id) + { + m_id = id; + m_established = false; + m_access_level = 0; + m_latency = 0; + m_creation_time = std::chrono::steady_clock::now(); + m_waiting_for_keep_alive_response = false; + m_maximum_packet_size = KI_DEFAULT_MAXIMUM_RECEIVE_SIZE; m_receive_state = ReceiveState::WAITING_FOR_START_SIGNAL; m_start_signal = 0; m_incoming_packet_size = 0; m_shift = 0; } - ParticipantType Participant::get_type() const - { - return m_type; - } - - void Participant::set_type(const ParticipantType type) - { - m_type = type; - } - - - uint16_t Participant::get_maximum_packet_size() const + uint16_t Session::get_maximum_packet_size() const { return m_maximum_packet_size; } - void Participant::set_maximum_packet_size(const uint16_t maximum_packet_size) + void Session::set_maximum_packet_size(const uint16_t maximum_packet_size) { m_maximum_packet_size = maximum_packet_size; } - void Participant::send_data(const char* data, const size_t size) + uint16_t Session::get_id() const + { + return m_id; + } + + bool Session::is_established() const + { + return m_established; + } + + uint8_t Session::get_access_level() const + { + return m_access_level; + } + + void Session::set_access_level(const uint8_t access_level) + { + m_access_level = access_level; + } + + uint16_t Session::get_latency() const + { + return m_latency; + } + + void Session::send_packet(const bool is_control, const uint8_t opcode, + const util::Serializable& data) + { + std::ostringstream ss; + PacketHeader header(is_control, opcode); + header.write_to(ss); + data.write_to(ss); + + const auto buffer = ss.str(); + send_data(buffer.c_str(), buffer.length()); + } + + void Session::send_data(const char* data, const size_t size) { // Allocate the entire buffer char *packet_data = new char[size + 4]; - + // Add the frame header ((uint16_t *)packet_data)[0] = KI_START_SIGNAL; ((uint16_t *)packet_data)[1] = size; @@ -55,7 +85,7 @@ namespace net delete[] packet_data; } - void Participant::process_data(const char *data, const size_t size) + void Session::process_data(const char *data, const size_t size) { size_t position = 0; while (position < size) @@ -133,6 +163,29 @@ namespace net } } + void Session::on_packet_available() + { + // Read the packet header + PacketHeader header; + try + { + header.read_from(m_data_stream); + } + catch (parse_error &e) + { + on_invalid_packet(); + return; + } + + // Hand off to the right handler based on + // whether this is a control packet or not + if (header.is_control()) + on_control_message(header); + else if (m_established) + on_application_message(header); + else + close(); + } } } } From 0eb81803c75a9fb59b9b902f82ef9a88b96a5d27 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Sun, 15 Apr 2018 22:58:50 +0100 Subject: [PATCH 39/49] net: Fix memcpy build error on Linux I swear I already did this... --- src/protocol/net/Session.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/protocol/net/Session.cpp b/src/protocol/net/Session.cpp index cbfbbd3..f5467c3 100644 --- a/src/protocol/net/Session.cpp +++ b/src/protocol/net/Session.cpp @@ -1,5 +1,6 @@ #include "ki/protocol/net/Session.h" #include "ki/protocol/exception.h" +#include namespace ki { From d7f5196a3620b326b60ce6385e68687b6c21988b Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 16 Apr 2018 19:02:03 +0100 Subject: [PATCH 40/49] test: Add tests for control message serialization/deserialization --- test/src/unit-protocol.cpp | 188 +++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 test/src/unit-protocol.cpp diff --git a/test/src/unit-protocol.cpp b/test/src/unit-protocol.cpp new file mode 100644 index 0000000..ec69ca9 --- /dev/null +++ b/test/src/unit-protocol.cpp @@ -0,0 +1,188 @@ +#define CATCH_CONFIG_MAIN +#include +#include + +#include +#include +#include +#include + +using namespace ki::protocol; + +TEST_CASE("Control Message Serialization", "[control]") +{ + std::ostringstream oss; + + SECTION("SessionOffer") + { + control::SessionOffer offer(0xABCD, 0xAABBCCDD, 0xAABBCCDD); + offer.write_to(oss); + + const char expected_bytes[] = { + // Session ID + 0xCD, 0xAB, + + // Unknown + 0x00, 0x00, 0x00, 0x00, + + // Timestamp + 0xDD, 0xCC, 0xBB, 0xAA, + + // Milliseconds + 0xDD, 0xCC, 0xBB, 0xAA + }; + REQUIRE(oss.str() == std::string(expected_bytes, sizeof(expected_bytes))); + } + + SECTION("SessionAccept") + { + control::SessionAccept accept(0xABCD, 0xAABBCCDD, 0xAABBCCDD); + accept.write_to(oss); + + const char expected_bytes[] = { + // Unknown + 0x00, 0x00, + + // Unknown + 0x00, 0x00, 0x00, 0x00, + + // Timestamp + 0xDD, 0xCC, 0xBB, 0xAA, + + // Milliseconds + 0xDD, 0xCC, 0xBB, 0xAA, + + // Session ID + 0xCD, 0xAB + }; + REQUIRE(oss.str() == std::string(expected_bytes, sizeof(expected_bytes))); + } + + SECTION("ClientKeepAlive") + { + control::ClientKeepAlive keep_alive(0xABCD, 0xABCD, 0xABCD); + keep_alive.write_to(oss); + + const char expected_bytes[] = { + // Session ID + 0xCD, 0xAB, + + // Milliseconds + 0xCD, 0xAB, + + // Minutes + 0xCD, 0xAB + }; + REQUIRE(oss.str() == std::string(expected_bytes, sizeof(expected_bytes))); + } + + SECTION("ServerKeepAlive") + { + control::ServerKeepAlive keep_alive(0xAABBCCDD); + keep_alive.write_to(oss); + + const char expected_bytes[] = { + // Unknown + 0x00, 0x00, + + // Timestamp + 0xDD, 0xCC, 0xBB, 0xAA + }; + REQUIRE(oss.str() == std::string(expected_bytes, sizeof(expected_bytes))); + } +} + +TEST_CASE("Control Message Deserialization", "[control]") +{ + SECTION("SessionOffer") + { + const char bytes[] = { + // Session ID + 0xCD, 0xAB, + + // Unknown + 0x00, 0x00, 0x00, 0x00, + + // Timestamp + 0xDD, 0xCC, 0xBB, 0xAA, + + // Milliseconds + 0xDD, 0xCC, 0xBB, 0xAA + }; + std::istringstream iss(std::string(bytes, sizeof(bytes))); + + control::SessionOffer offer; + offer.read_from(iss); + + REQUIRE(offer.get_session_id() == 0xABCD); + REQUIRE(offer.get_timestamp() == 0xAABBCCDD); + REQUIRE(offer.get_milliseconds() == 0xAABBCCDD); + } + + SECTION("SessionAccept") + { + const char bytes[] = { + // Unknown + 0x00, 0x00, + + // Unknown + 0x00, 0x00, 0x00, 0x00, + + // Timestamp + 0xDD, 0xCC, 0xBB, 0xAA, + + // Milliseconds + 0xDD, 0xCC, 0xBB, 0xAA, + + // Session ID + 0xCD, 0xAB + }; + std::istringstream iss(std::string(bytes, sizeof(bytes))); + + control::SessionAccept accept; + accept.read_from(iss); + + REQUIRE(accept.get_session_id() == 0xABCD); + REQUIRE(accept.get_timestamp() == 0xAABBCCDD); + REQUIRE(accept.get_milliseconds() == 0xAABBCCDD); + } + + SECTION("ClientKeepAlive") + { + const char bytes[] = { + // Session ID + 0xCD, 0xAB, + + // Milliseconds + 0xCD, 0xAB, + + // Minutes + 0xCD, 0xAB + }; + std::istringstream iss(std::string(bytes, sizeof(bytes))); + + control::ClientKeepAlive keep_alive; + keep_alive.read_from(iss); + + REQUIRE(keep_alive.get_session_id() == 0xABCD); + REQUIRE(keep_alive.get_milliseconds() == 0xABCD); + REQUIRE(keep_alive.get_minutes() == 0xABCD); + } + + SECTION("ServerKeepAlive") + { + const char bytes[] = { + // Unknown + 0x00, 0x00, + + // Timestamp + 0xDD, 0xCC, 0xBB, 0xAA + }; + std::istringstream iss(std::string(bytes, sizeof(bytes))); + + control::ServerKeepAlive keep_alive; + keep_alive.read_from(iss); + + REQUIRE(keep_alive.get_timestamp() == 0xAABBCCDD); + } +} From 2632ef563cb4abcd6409791dd482a380f957f15d Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Mon, 16 Apr 2018 21:03:47 +0100 Subject: [PATCH 41/49] test: Fix narrowing errors in arary initialization --- test/src/unit-protocol.cpp | 58 +++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/test/src/unit-protocol.cpp b/test/src/unit-protocol.cpp index ec69ca9..2c54a0d 100644 --- a/test/src/unit-protocol.cpp +++ b/test/src/unit-protocol.cpp @@ -20,16 +20,16 @@ TEST_CASE("Control Message Serialization", "[control]") const char expected_bytes[] = { // Session ID - 0xCD, 0xAB, + '\xCD', '\xAB', // Unknown - 0x00, 0x00, 0x00, 0x00, + '\x00', '\x00', '\x00', '\x00', // Timestamp - 0xDD, 0xCC, 0xBB, 0xAA, + '\xDD', '\xCC', '\xBB', '\xAA', // Milliseconds - 0xDD, 0xCC, 0xBB, 0xAA + '\xDD', '\xCC', '\xBB', '\xAA' }; REQUIRE(oss.str() == std::string(expected_bytes, sizeof(expected_bytes))); } @@ -41,19 +41,19 @@ TEST_CASE("Control Message Serialization", "[control]") const char expected_bytes[] = { // Unknown - 0x00, 0x00, + '\x00', '\x00', // Unknown - 0x00, 0x00, 0x00, 0x00, + '\x00', '\x00', '\x00', '\x00', // Timestamp - 0xDD, 0xCC, 0xBB, 0xAA, + '\xDD', '\xCC', '\xBB', '\xAA', // Milliseconds - 0xDD, 0xCC, 0xBB, 0xAA, + '\xDD', '\xCC', '\xBB', '\xAA', // Session ID - 0xCD, 0xAB + '\xCD', '\xAB' }; REQUIRE(oss.str() == std::string(expected_bytes, sizeof(expected_bytes))); } @@ -65,13 +65,13 @@ TEST_CASE("Control Message Serialization", "[control]") const char expected_bytes[] = { // Session ID - 0xCD, 0xAB, + '\xCD', '\xAB', // Milliseconds - 0xCD, 0xAB, + '\xCD', '\xAB', // Minutes - 0xCD, 0xAB + '\xCD', '\xAB' }; REQUIRE(oss.str() == std::string(expected_bytes, sizeof(expected_bytes))); } @@ -83,31 +83,31 @@ TEST_CASE("Control Message Serialization", "[control]") const char expected_bytes[] = { // Unknown - 0x00, 0x00, + '\x00', '\x00', // Timestamp - 0xDD, 0xCC, 0xBB, 0xAA + '\xDD', '\xCC', '\xBB', '\xAA' }; REQUIRE(oss.str() == std::string(expected_bytes, sizeof(expected_bytes))); } } TEST_CASE("Control Message Deserialization", "[control]") -{ +{ SECTION("SessionOffer") { const char bytes[] = { // Session ID - 0xCD, 0xAB, + '\xCD', '\xAB', // Unknown - 0x00, 0x00, 0x00, 0x00, + '\x00', '\x00', '\x00', '\x00', // Timestamp - 0xDD, 0xCC, 0xBB, 0xAA, + '\xDD', '\xCC', '\xBB', '\xAA', // Milliseconds - 0xDD, 0xCC, 0xBB, 0xAA + '\xDD', '\xCC', '\xBB', '\xAA' }; std::istringstream iss(std::string(bytes, sizeof(bytes))); @@ -123,19 +123,19 @@ TEST_CASE("Control Message Deserialization", "[control]") { const char bytes[] = { // Unknown - 0x00, 0x00, + '\x00', '\x00', // Unknown - 0x00, 0x00, 0x00, 0x00, + '\x00', '\x00', '\x00', '\x00', // Timestamp - 0xDD, 0xCC, 0xBB, 0xAA, + '\xDD', '\xCC', '\xBB', '\xAA', // Milliseconds - 0xDD, 0xCC, 0xBB, 0xAA, + '\xDD', '\xCC', '\xBB', '\xAA', // Session ID - 0xCD, 0xAB + '\xCD', '\xAB' }; std::istringstream iss(std::string(bytes, sizeof(bytes))); @@ -151,13 +151,13 @@ TEST_CASE("Control Message Deserialization", "[control]") { const char bytes[] = { // Session ID - 0xCD, 0xAB, + '\xCD', '\xAB', // Milliseconds - 0xCD, 0xAB, + '\xCD', '\xAB', // Minutes - 0xCD, 0xAB + '\xCD', '\xAB' }; std::istringstream iss(std::string(bytes, sizeof(bytes))); @@ -173,10 +173,10 @@ TEST_CASE("Control Message Deserialization", "[control]") { const char bytes[] = { // Unknown - 0x00, 0x00, + '\x00', '\x00', // Timestamp - 0xDD, 0xCC, 0xBB, 0xAA + '\xDD', '\xCC', '\xBB', '\xAA' }; std::istringstream iss(std::string(bytes, sizeof(bytes))); From d1118a937bb273b5ffdc04cddabb7dced82fce10 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 20 Apr 2018 01:35:05 +0100 Subject: [PATCH 42/49] protocol: Messages must now be created from a MessageTemplate It shouldn't have been possible to create a Message manually. --- include/ki/protocol/dml/Message.h | 47 +++---- include/ki/protocol/dml/MessageBuilder.h | 42 ------ include/ki/protocol/dml/MessageHeader.h | 37 +++++ include/ki/protocol/dml/MessageManager.h | 10 +- include/ki/protocol/dml/MessageModule.h | 5 +- include/ki/protocol/dml/MessageTemplate.h | 10 +- src/protocol/CMakeLists.txt | 2 +- src/protocol/dml/Message.cpp | 164 +++++++++++++--------- src/protocol/dml/MessageBuilder.cpp | 38 ----- src/protocol/dml/MessageHeader.cpp | 88 ++++++++++++ src/protocol/dml/MessageManager.cpp | 51 ++++--- src/protocol/dml/MessageModule.cpp | 8 +- src/protocol/dml/MessageTemplate.cpp | 33 ++++- 13 files changed, 327 insertions(+), 208 deletions(-) delete mode 100644 include/ki/protocol/dml/MessageBuilder.h create mode 100644 include/ki/protocol/dml/MessageHeader.h delete mode 100644 src/protocol/dml/MessageBuilder.cpp create mode 100644 src/protocol/dml/MessageHeader.cpp diff --git a/include/ki/protocol/dml/Message.h b/include/ki/protocol/dml/Message.h index 096c96c..bddbb35 100644 --- a/include/ki/protocol/dml/Message.h +++ b/include/ki/protocol/dml/Message.h @@ -1,4 +1,5 @@ #pragma once +#include "MessageHeader.h" #include "../../util/Serializable.h" #include "../../dml/Record.h" #include @@ -9,49 +10,41 @@ namespace protocol { namespace dml { + class MessageTemplate; + class Message final : public util::Serializable { public: - Message(uint8_t service_id = 0, uint8_t type = 0); + Message(const MessageTemplate *message_template = nullptr); virtual ~Message(); - uint8_t get_service_id() const; - void set_service_id(uint8_t service_id); - - uint8_t get_type() const; - void set_type(uint8_t type); + const MessageTemplate *get_template() const; + void set_template(const MessageTemplate *message_template); ki::dml::Record *get_record(); const ki::dml::Record *get_record() const; - /** - * Sets the record to a copy of the specified record. - */ - void set_record(const ki::dml::Record &record); + ki::dml::FieldBase *get_field(std::string name); + const ki::dml::FieldBase *get_field(std::string name) const; - /** - * If raw data is present, then this uses the specified record - * to parse the raw DML message payload into a new Record. - * If raw data is not present, this is equivalent to set_record. - * - * If the raw data is parsed successfully, the internal raw - * data is cleared, and calls to get_record will return a valid - * Record pointer. - * - * However, if the raw data is not parsed successfully, then - * calls to get_record will still return nullptr. - */ - void use_template_record(const ki::dml::Record &record); + uint8_t get_service_id() const; + uint8_t get_type() const; + uint16_t get_message_size() const; + std::string get_handler() const; + uint8_t get_access_level() const; void write_to(std::ostream &ostream) const override final; void read_from(std::istream &istream) override final; size_t get_size() const override final; private: - uint8_t m_service_id; - uint8_t m_type; - std::vector m_raw_data; + const MessageTemplate *m_template; ki::dml::Record *m_record; + + // This is used to store raw data when a Message is + // constructed without a MessageTemplate. + MessageHeader m_header; + std::vector m_raw_data; }; } } -} \ No newline at end of file +} diff --git a/include/ki/protocol/dml/MessageBuilder.h b/include/ki/protocol/dml/MessageBuilder.h deleted file mode 100644 index 33995eb..0000000 --- a/include/ki/protocol/dml/MessageBuilder.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include "Message.h" -#include "ki/protocol/exception.h" -#include -#include - -namespace ki -{ -namespace protocol -{ -namespace dml -{ - class MessageBuilder - { - public: - MessageBuilder(uint8_t service_id = 0, uint8_t type = 0); - - MessageBuilder &set_service_id(uint8_t service_id); - MessageBuilder &set_message_type(uint8_t type); - MessageBuilder &use_template_record(const ki::dml::Record &record); - - template - MessageBuilder &set_field_value(std::string name, ValueT value) - { - auto *field = m_message->get_record()->get_field(name); - if (!field) - { - std::ostringstream oss; - oss << "No field with name " << name << " exists with specified type."; - throw value_error(oss.str()); - } - field->set_value(value); - return *this; - } - - Message *get_message() const; - private: - Message *m_message; - }; -} -} -} diff --git a/include/ki/protocol/dml/MessageHeader.h b/include/ki/protocol/dml/MessageHeader.h new file mode 100644 index 0000000..7bda780 --- /dev/null +++ b/include/ki/protocol/dml/MessageHeader.h @@ -0,0 +1,37 @@ +#pragma once +#include "../../util/Serializable.h" +#include + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + class MessageHeader : public util::Serializable + { + public: + MessageHeader(uint8_t service_id = 0, + uint8_t type = 0, uint16_t size = 0); + virtual ~MessageHeader() = default; + + uint8_t get_service_id() const; + void set_service_id(uint8_t service_id); + + uint8_t get_type() const; + void set_type(uint8_t type); + + uint16_t get_message_size() const; + void set_message_size(uint16_t size); + + void write_to(std::ostream &ostream) const override final; + void read_from(std::istream &istream) override final; + size_t get_size() const override final; + private: + uint8_t m_service_id; + uint8_t m_type; + uint16_t m_size; + }; +} +} +} \ No newline at end of file diff --git a/include/ki/protocol/dml/MessageManager.h b/include/ki/protocol/dml/MessageManager.h index 75e9e16..a051ba2 100644 --- a/include/ki/protocol/dml/MessageManager.h +++ b/include/ki/protocol/dml/MessageManager.h @@ -1,6 +1,6 @@ #pragma once +#include "Message.h" #include "MessageModule.h" -#include "MessageBuilder.h" #include "../../dml/Record.h" #include @@ -20,10 +20,10 @@ namespace dml const MessageModule *get_module(uint8_t service_id) const; const MessageModule *get_module(const std::string &protocol_type) const; - MessageBuilder &build_message(uint8_t service_id, uint8_t message_type) const; - MessageBuilder &build_message(uint8_t service_id, const std::string &message_name) const; - MessageBuilder &build_message(const std::string &protocol_type, uint8_t message_type) const; - MessageBuilder &build_message(const std::string &protocol_type, const std::string &message_name) const; + Message *create_message(uint8_t service_id, uint8_t message_type) const; + Message *create_message(uint8_t service_id, const std::string &message_name) const; + Message *create_message(const std::string &protocol_type, uint8_t message_type) const; + Message *create_message(const std::string &protocol_type, const std::string &message_name) const; /** * If the DML message header cannot be read, then a nullptr diff --git a/include/ki/protocol/dml/MessageModule.h b/include/ki/protocol/dml/MessageModule.h index 3b763c3..c58e080 100644 --- a/include/ki/protocol/dml/MessageModule.h +++ b/include/ki/protocol/dml/MessageModule.h @@ -1,4 +1,5 @@ #pragma once +#include "Message.h" #include "MessageTemplate.h" #include #include @@ -33,8 +34,8 @@ namespace dml void sort_lookup(); - MessageBuilder &build_message(uint8_t message_type) const; - MessageBuilder &build_message(std::string message_name) const; + Message *create_message(uint8_t message_type) const; + Message *create_message(std::string message_name) const; private: uint8_t m_service_id; std::string m_protocol_type; diff --git a/include/ki/protocol/dml/MessageTemplate.h b/include/ki/protocol/dml/MessageTemplate.h index 5cf3375..3f82ab2 100644 --- a/include/ki/protocol/dml/MessageTemplate.h +++ b/include/ki/protocol/dml/MessageTemplate.h @@ -1,6 +1,6 @@ #pragma once #include "../../dml/Record.h" -#include "MessageBuilder.h" +#include "Message.h" #include namespace ki @@ -25,10 +25,16 @@ namespace dml uint8_t get_service_id() const; void set_service_id(uint8_t service_id); + std::string get_handler() const; + void set_handler(std::string handler); + + uint8_t get_access_level() const; + void set_access_level(uint8_t access_level); + const ki::dml::Record &get_record() const; void set_record(ki::dml::Record *record); - MessageBuilder &build_message() const; + Message *create_message() const; private: std::string m_name; uint8_t m_type; diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt index 79c513c..191b07e 100644 --- a/src/protocol/CMakeLists.txt +++ b/src/protocol/CMakeLists.txt @@ -5,7 +5,7 @@ target_sources(${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/src/protocol/control/SessionAccept.cpp ${PROJECT_SOURCE_DIR}/src/protocol/control/SessionOffer.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/Message.cpp - ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageBuilder.cpp + ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageHeader.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageManager.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageModule.cpp ${PROJECT_SOURCE_DIR}/src/protocol/dml/MessageTemplate.cpp diff --git a/src/protocol/dml/Message.cpp b/src/protocol/dml/Message.cpp index 107dc56..27d4110 100644 --- a/src/protocol/dml/Message.cpp +++ b/src/protocol/dml/Message.cpp @@ -1,4 +1,5 @@ #include "ki/protocol/dml/Message.h" +#include "ki/protocol/dml/MessageTemplate.h" #include "ki/protocol/exception.h" namespace ki @@ -7,11 +8,13 @@ namespace protocol { namespace dml { - Message::Message(uint8_t service_id, uint8_t type) + Message::Message(const MessageTemplate *message_template) { - m_service_id = service_id; - m_type = type; - m_record = nullptr; + m_template = message_template; + if (m_template) + m_record = new ki::dml::Record(m_template->get_record()); + else + m_record = nullptr; } Message::~Message() @@ -19,24 +22,72 @@ namespace dml delete m_record; } - uint8_t Message::get_service_id() const + const MessageTemplate *Message::get_template() const { - return m_service_id; + return m_template; } - void Message::set_service_id(uint8_t service_id) + void Message::set_template(const MessageTemplate *message_template) { - m_service_id = service_id; + m_template = message_template; + if (!m_template) + return; + + m_record = new ki::dml::Record(message_template->get_record()); + if (!m_raw_data.empty()) + { + std::istringstream iss(std::string(m_raw_data.data(), m_raw_data.size())); + try + { + m_record->read_from(iss); + m_raw_data.clear(); + } + catch (ki::dml::parse_error &e) + { + delete m_record; + m_template = nullptr; + m_record = nullptr; + + std::ostringstream oss; + oss << "Error reading DML message payload: " << e.what(); + throw parse_error(oss.str()); + } + } + } + + uint8_t Message::get_service_id() const + { + if (m_template) + return m_template->get_service_id(); + return m_header.get_service_id(); } uint8_t Message::get_type() const { - return m_type; + if (m_template) + return m_template->get_type(); + return m_header.get_type(); } - void Message::set_type(uint8_t type) + uint16_t Message::get_message_size() const { - m_type = type; + if (m_record) + return m_record->get_size(); + return m_raw_data.size(); + } + + std::string Message::get_handler() const + { + if (m_template) + return m_template->get_handler(); + return ""; + } + + uint8_t Message::get_access_level() const + { + if (m_template) + return m_template->get_access_level(); + return 0; } ki::dml::Record *Message::get_record() @@ -49,82 +100,69 @@ namespace dml return m_record; } - void Message::set_record(const ki::dml::Record &record) + ki::dml::FieldBase* Message::get_field(std::string name) { - m_record = new ki::dml::Record(record); + if (m_record) + return m_record->get_field(name); + return nullptr; } - void Message::use_template_record(const ki::dml::Record &record) + const ki::dml::FieldBase* Message::get_field(std::string name) const { - set_record(record); - if (!m_raw_data.empty()) - { - std::istringstream iss(std::string(m_raw_data.data(), m_raw_data.size())); - try - { - m_record->read_from(iss); - m_raw_data.clear(); - } - catch (ki::dml::parse_error &e) - { - delete m_record; - m_record = nullptr; - - std::ostringstream oss; - oss << "Error reading DML message payload: " << e.what(); - throw parse_error(oss.str()); - } - } + if (m_record) + return m_record->get_field(name); + return nullptr; } void Message::write_to(std::ostream &ostream) const { - ki::dml::Record record; - record.add_field("m_service_id")->set_value(m_service_id); - record.add_field("m_type")->set_value(m_type); - auto *size_field = record.add_field("size"); - if (m_record) - size_field->set_value(m_record->get_size() + 4); + // Write the header + if (m_template) + { + MessageHeader header( + get_service_id(), get_type(), get_message_size()); + header.write_to(ostream); + } else - size_field->set_value(m_raw_data.size() + 4); - record.write_to(ostream); + m_header.write_to(ostream); + // Write the payload if (m_record) - record.write_to(ostream); + m_record->write_to(ostream); else ostream.write(m_raw_data.data(), m_raw_data.size()); } void Message::read_from(std::istream &istream) { - ki::dml::Record record; - auto *service_id_field = record.add_field("ServiceID"); - auto *message_type_field = record.add_field("MsgType"); - auto *size_field = record.add_field("Length"); - try + m_header.read_from(istream); + if (m_template) { - record.read_from(istream); - } - catch (ki::dml::parse_error &e) - { - std::ostringstream oss; - oss << "Error reading DML message header: " << e.what(); - throw parse_error(oss.str()); - } + // Check for mismatches between the header and template + if (m_header.get_service_id() != m_template->get_service_id()) + throw value_error("ServiceID mismatch between MessageHeader and assigned template."); + if (m_header.get_type() != m_template->get_type()) + throw value_error("Message Type mismatch between MessageHeader and assigned template."); - m_service_id = service_id_field->get_value(); - m_type = message_type_field->get_value(); - const ki::dml::USHRT size = size_field->get_value() - 4; - m_raw_data.resize(size); - istream.read(m_raw_data.data(), size); - if (istream.fail()) - throw parse_error("Not enough data was available to read DML message payload."); + // Read the payload into the record + m_record->read_from(istream); + } + else + { + // We don't have a template for the record structure, so + // just read the raw data into a buffer. + const auto size = m_header.get_message_size(); + m_raw_data.resize(size); + istream.read(m_raw_data.data(), size); + if (istream.fail()) + throw parse_error("Not enough data was available to read DML message payload."); + } } size_t Message::get_size() const { if (m_record) - return 4 + m_record->get_size(); + return m_header.get_size() + m_record->get_size(); return 4 + m_raw_data.size(); } } diff --git a/src/protocol/dml/MessageBuilder.cpp b/src/protocol/dml/MessageBuilder.cpp deleted file mode 100644 index aaed66f..0000000 --- a/src/protocol/dml/MessageBuilder.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "ki/protocol/dml/MessageBuilder.h" - -namespace ki -{ -namespace protocol -{ -namespace dml -{ - MessageBuilder::MessageBuilder(uint8_t service_id, uint8_t type) - { - m_message = new Message(service_id, type); - } - - MessageBuilder &MessageBuilder::set_service_id(uint8_t service_id) - { - m_message->set_service_id(service_id); - return *this; - } - - MessageBuilder &MessageBuilder::set_message_type(uint8_t type) - { - m_message->set_type(type); - return *this; - } - - MessageBuilder &MessageBuilder::use_template_record(const ki::dml::Record& record) - { - m_message->set_record(record); - return *this; - } - - Message *MessageBuilder::get_message() const - { - return m_message; - } -} -} -} \ No newline at end of file diff --git a/src/protocol/dml/MessageHeader.cpp b/src/protocol/dml/MessageHeader.cpp new file mode 100644 index 0000000..46290a3 --- /dev/null +++ b/src/protocol/dml/MessageHeader.cpp @@ -0,0 +1,88 @@ +#include "ki/protocol/dml/MessageHeader.h" +#include "ki/dml/Record.h" +#include "ki/protocol/exception.h" + +namespace ki +{ +namespace protocol +{ +namespace dml +{ + MessageHeader::MessageHeader(const uint8_t service_id, + const uint8_t type, const uint16_t size) + { + m_service_id = service_id; + m_type = type; + m_size = size; + } + + uint8_t MessageHeader::get_service_id() const + { + return m_service_id; + } + + void MessageHeader::set_service_id(const uint8_t service_id) + { + m_service_id = service_id; + } + + uint8_t MessageHeader::get_type() const + { + return m_type; + } + + void MessageHeader::set_type(const uint8_t type) + { + m_type = type; + } + + uint16_t MessageHeader::get_message_size() const + { + return m_size; + } + + void MessageHeader::set_message_size(const uint16_t size) + { + m_size = size; + } + + void MessageHeader::write_to(std::ostream& ostream) const + { + ki::dml::Record record; + record.add_field("m_service_id")->set_value(m_service_id); + record.add_field("m_type")->set_value(m_type); + record.add_field("m_size")->set_value(m_size + 4); + record.write_to(ostream); + } + + void MessageHeader::read_from(std::istream& istream) + { + ki::dml::Record record; + const auto *service_id = record.add_field("m_service_id"); + const auto *type = record.add_field("m_type"); + const auto size = record.add_field("m_size"); + + try + { + record.read_from(istream); + } + catch (ki::dml::parse_error &e) + { + std::ostringstream oss; + oss << "Error reading MessageHeader: " << e.what(); + throw parse_error(oss.str()); + } + + m_service_id = service_id->get_value(); + m_type = type->get_value(); + m_size = size->get_value() - 4; + } + + size_t MessageHeader::get_size() const + { + return sizeof(ki::dml::UBYT) + sizeof(ki::dml::UBYT) + + sizeof(ki::dml::USHRT); + } +} +} +} diff --git a/src/protocol/dml/MessageManager.cpp b/src/protocol/dml/MessageManager.cpp index 90903ae..328e597 100644 --- a/src/protocol/dml/MessageManager.cpp +++ b/src/protocol/dml/MessageManager.cpp @@ -1,4 +1,5 @@ #include "ki/protocol/dml/MessageManager.h" +#include "ki/protocol/dml/MessageHeader.h" #include "ki/protocol/exception.h" #include "ki/dml/Record.h" #include "ki/util/ValueBytes.h" @@ -155,7 +156,7 @@ namespace dml return nullptr; } - MessageBuilder &MessageManager::build_message(uint8_t service_id, uint8_t message_type) const + Message *MessageManager::create_message(uint8_t service_id, uint8_t message_type) const { auto *message_module = get_module(service_id); if (!message_module) @@ -165,10 +166,10 @@ namespace dml throw value_error(oss.str()); } - return message_module->build_message(message_type); + return message_module->create_message(message_type); } - MessageBuilder& MessageManager::build_message(uint8_t service_id, const std::string& message_name) const + Message *MessageManager::create_message(uint8_t service_id, const std::string& message_name) const { auto *message_module = get_module(service_id); if (!message_module) @@ -178,10 +179,10 @@ namespace dml throw value_error(oss.str()); } - return message_module->build_message(message_name); + return message_module->create_message(message_name); } - MessageBuilder& MessageManager::build_message(const std::string& protocol_type, uint8_t message_type) const + Message *MessageManager::create_message(const std::string& protocol_type, uint8_t message_type) const { auto *message_module = get_module(protocol_type); if (!message_module) @@ -191,10 +192,10 @@ namespace dml throw value_error(oss.str()); } - return message_module->build_message(message_type); + return message_module->create_message(message_type); } - MessageBuilder& MessageManager::build_message(const std::string& protocol_type, const std::string& message_name) const + Message *MessageManager::create_message(const std::string& protocol_type, const std::string& message_name) const { auto *message_module = get_module(protocol_type); if (!message_module) @@ -204,37 +205,49 @@ namespace dml throw value_error(oss.str()); } - return message_module->build_message(message_name); + return message_module->create_message(message_name); } const Message *MessageManager::message_from_binary(std::istream& istream) const { - // Read the message header and raw payload - Message *message = new Message(); + // Read the message header + MessageHeader header; try { - message->read_from(istream); + header.read_from(istream); } catch (parse_error &e) { - delete message; return nullptr; } // Get the message module that uses the specified service id - auto *message_module = get_module(message->get_service_id()); + auto *message_module = get_module(header.get_service_id()); if (!message_module) - return message; + return nullptr; // Get the message template for this message type - auto *message_template = message_module->get_message_template(message->get_type()); + auto *message_template = message_module->get_message_template(header.get_type()); if (!message_template) - return message; + return nullptr; - // Parse the raw payload with the template - message->use_template_record(message_template->get_record()); + // Make sure that the size specified is enough to read this message + if (header.get_message_size() < message_template->get_record().get_size()) + return nullptr; + + // Create a new Message from the template + auto *message = new Message(message_template); + try + { + message->get_record()->read_from(istream); + } + catch (ki::dml::parse_error &e) + { + delete message; + return nullptr; + } return message; } } } -} \ No newline at end of file +} diff --git a/src/protocol/dml/MessageModule.cpp b/src/protocol/dml/MessageModule.cpp index 6345507..11a48c0 100644 --- a/src/protocol/dml/MessageModule.cpp +++ b/src/protocol/dml/MessageModule.cpp @@ -139,7 +139,7 @@ namespace dml } } - MessageBuilder& MessageModule::build_message(uint8_t message_type) const + Message *MessageModule::create_message(uint8_t message_type) const { auto *message_template = get_message_template(message_type); if (!message_template) @@ -150,10 +150,10 @@ namespace dml throw value_error(oss.str()); } - return message_template->build_message(); + return message_template->create_message(); } - MessageBuilder &MessageModule::build_message(std::string message_name) const + Message *MessageModule::create_message(std::string message_name) const { auto *message_template = get_message_template(message_name); if (!message_template) @@ -164,7 +164,7 @@ namespace dml throw value_error(oss.str()); } - return message_template->build_message(); + return message_template->create_message(); } } } diff --git a/src/protocol/dml/MessageTemplate.cpp b/src/protocol/dml/MessageTemplate.cpp index e89db88..9e79929 100644 --- a/src/protocol/dml/MessageTemplate.cpp +++ b/src/protocol/dml/MessageTemplate.cpp @@ -50,6 +50,32 @@ namespace dml m_service_id = service_id; } + std::string MessageTemplate::get_handler() const + { + const auto field = m_record->get_field("_MsgHandler"); + if (field) + return field->get_value(); + return m_name; + } + + void MessageTemplate::set_handler(std::string handler) + { + m_record->add_field("_MsgHandler")->set_value(handler); + } + + uint8_t MessageTemplate::get_access_level() const + { + const auto field = m_record->get_field("_MsgAccessLvl"); + if (field) + return field->get_value(); + return 0; + } + + void MessageTemplate::set_access_level(uint8_t access_level) + { + m_record->add_field("_MsgAccessLvl")->set_value(access_level); + } + const ki::dml::Record& MessageTemplate::get_record() const { return *m_record; @@ -60,12 +86,9 @@ namespace dml m_record = record; } - MessageBuilder &MessageTemplate::build_message() const + Message *MessageTemplate::create_message() const { - return MessageBuilder() - .set_message_type(m_type) - .set_service_id(m_service_id) - .use_template_record(*m_record); + return new Message(this); } } } From 1927426034466397ba526dcfb1902f8a0ba9f183 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 20 Apr 2018 01:35:43 +0100 Subject: [PATCH 43/49] net: Fix some observed problems It seems the client sends application messages before the session has been established. --- src/protocol/net/DMLSession.cpp | 6 +++++- src/protocol/net/Session.cpp | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/protocol/net/DMLSession.cpp b/src/protocol/net/DMLSession.cpp index a0e4b85..54fc9a4 100644 --- a/src/protocol/net/DMLSession.cpp +++ b/src/protocol/net/DMLSession.cpp @@ -23,7 +23,11 @@ namespace net return; } - on_message(*message); + // Are we sufficiently authenticated to handle this message? + if (get_access_level() >= message->get_access_level()) + on_message(*message); + else + on_invalid_message(); delete message; } } diff --git a/src/protocol/net/Session.cpp b/src/protocol/net/Session.cpp index f5467c3..6a995c8 100644 --- a/src/protocol/net/Session.cpp +++ b/src/protocol/net/Session.cpp @@ -182,10 +182,8 @@ namespace net // whether this is a control packet or not if (header.is_control()) on_control_message(header); - else if (m_established) - on_application_message(header); else - close(); + on_application_message(header); } } } From e26a20d650e8f414383f70f8b7bd9e62228567d1 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 20 Apr 2018 17:12:31 +0100 Subject: [PATCH 44/49] net: Add a method to get the MessageManager instance from a DMLSession --- include/ki/protocol/net/DMLSession.h | 2 ++ src/protocol/net/DMLSession.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/include/ki/protocol/net/DMLSession.h b/include/ki/protocol/net/DMLSession.h index b5a6a32..8245dc6 100644 --- a/include/ki/protocol/net/DMLSession.h +++ b/include/ki/protocol/net/DMLSession.h @@ -18,6 +18,8 @@ namespace net DMLSession(uint16_t id, const dml::MessageManager &manager); virtual ~DMLSession() = default; + const dml::MessageManager &get_manager() const; + void send_message(const dml::Message &message); protected: void on_application_message(const PacketHeader& header) override; diff --git a/src/protocol/net/DMLSession.cpp b/src/protocol/net/DMLSession.cpp index 54fc9a4..f11a7ed 100644 --- a/src/protocol/net/DMLSession.cpp +++ b/src/protocol/net/DMLSession.cpp @@ -9,6 +9,11 @@ namespace net DMLSession::DMLSession(const uint16_t id, const dml::MessageManager& manager) : Session(id), m_manager(manager) {} + const dml::MessageManager& DMLSession::get_manager() const + { + return m_manager; + } + void DMLSession::send_message(const dml::Message& message) { send_packet(false, 0, message); From 7932ee2831e59b81b234f1aa6f400f1f95c98269 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 20 Apr 2018 17:22:51 +0100 Subject: [PATCH 45/49] net: Add error codes to DMLSession::invalid_message I'm going to rethink this though because I'd like to have more detailed error codes. --- include/ki/protocol/net/DMLSession.h | 8 +++++++- src/protocol/net/DMLSession.cpp | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/ki/protocol/net/DMLSession.h b/include/ki/protocol/net/DMLSession.h index 8245dc6..345b563 100644 --- a/include/ki/protocol/net/DMLSession.h +++ b/include/ki/protocol/net/DMLSession.h @@ -8,6 +8,12 @@ namespace protocol { namespace net { + enum class InvalidDMLMessageErrorCode + { + INVALID_MESSAGE_DATA, + INSUFFICIENT_ACCESS + }; + /** * Implements an application protocol that uses the DML * message system (as seen in Wizard101 and Pirate101). @@ -24,7 +30,7 @@ namespace net protected: void on_application_message(const PacketHeader& header) override; virtual void on_message(const dml::Message &message) {} - virtual void on_invalid_message() {} + virtual void on_invalid_message(InvalidDMLMessageErrorCode error) {} private: const dml::MessageManager &m_manager; }; diff --git a/src/protocol/net/DMLSession.cpp b/src/protocol/net/DMLSession.cpp index f11a7ed..aec5c48 100644 --- a/src/protocol/net/DMLSession.cpp +++ b/src/protocol/net/DMLSession.cpp @@ -21,10 +21,11 @@ namespace net void DMLSession::on_application_message(const PacketHeader& header) { + // Attempt to create a Message instance from the data in the stream const auto *message = m_manager.message_from_binary(m_data_stream); if (!message) { - on_invalid_message(); + on_invalid_message(InvalidDMLMessageErrorCode::INVALID_MESSAGE_DATA); return; } @@ -32,7 +33,7 @@ namespace net if (get_access_level() >= message->get_access_level()) on_message(*message); else - on_invalid_message(); + on_invalid_message(InvalidDMLMessageErrorCode::INSUFFICIENT_ACCESS); delete message; } } From 993191ec51d0499c571edbc452b26af66a174724 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 20 Apr 2018 18:53:23 +0100 Subject: [PATCH 46/49] net: Add error codes to Session::close --- include/ki/protocol/net/Session.h | 18 +++++++++++++++++- src/protocol/net/ClientSession.cpp | 12 ++++++------ src/protocol/net/ServerSession.cpp | 12 ++++++------ src/protocol/net/Session.cpp | 4 ++-- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/include/ki/protocol/net/Session.h b/include/ki/protocol/net/Session.h index f3b7682..e877319 100644 --- a/include/ki/protocol/net/Session.h +++ b/include/ki/protocol/net/Session.h @@ -17,6 +17,22 @@ namespace protocol { namespace net { + enum class SessionCloseErrorCode + { + NONE, + APPLICATION_ERROR, + + INVALID_FRAMING_START_SIGNAL, + INVALID_FRAMING_SIZE_EXCEEDS_MAXIMUM, + + UNHANDLED_CONTROL_MESSAGE, + UNHANDLED_APPLICATION_MESSAGE, + INVALID_MESSAGE, + + SESSION_OFFER_TIMED_OUT, + SESSION_DIED + }; + enum class ReceiveState { // Waiting for the 0xF00D start signal. @@ -106,7 +122,7 @@ namespace net /* Low-level socket methods */ virtual void send_packet_data(const char *data, const size_t size) = 0; - virtual void close() = 0; + virtual void close(SessionCloseErrorCode error) = 0; private: /* Low-level networking members */ uint16_t m_maximum_packet_size; diff --git a/src/protocol/net/ClientSession.cpp b/src/protocol/net/ClientSession.cpp index db70956..9232a5a 100644 --- a/src/protocol/net/ClientSession.cpp +++ b/src/protocol/net/ClientSession.cpp @@ -72,7 +72,7 @@ namespace net break; default: - close(); + close(SessionCloseErrorCode::UNHANDLED_CONTROL_MESSAGE); break; } } @@ -87,9 +87,9 @@ namespace net } catch (parse_error &e) { - // The SESSION_ACCEPT wasn't valid... + // The SESSION_OFFER wasn't valid... // Close the session - close(); + close(SessionCloseErrorCode::INVALID_MESSAGE); return; } @@ -98,7 +98,7 @@ namespace net std::chrono::steady_clock::now() - m_connection_time ).count() > KI_CONNECTION_TIMEOUT) { - close(); + close(SessionCloseErrorCode::SESSION_OFFER_TIMED_OUT); return; } @@ -136,7 +136,7 @@ namespace net { // The KEEP_ALIVE wasn't valid... // Close the session - close(); + close(SessionCloseErrorCode::INVALID_MESSAGE); return; } @@ -158,7 +158,7 @@ namespace net { // The KEEP_ALIVE_RSP wasn't valid... // Close the session - close(); + close(SessionCloseErrorCode::INVALID_MESSAGE); return; } diff --git a/src/protocol/net/ServerSession.cpp b/src/protocol/net/ServerSession.cpp index 5da2d09..72efe98 100644 --- a/src/protocol/net/ServerSession.cpp +++ b/src/protocol/net/ServerSession.cpp @@ -78,7 +78,7 @@ namespace net break; default: - close(); + close(SessionCloseErrorCode::UNHANDLED_CONTROL_MESSAGE); break; } } @@ -95,7 +95,7 @@ namespace net { // The SESSION_ACCEPT wasn't valid... // Close the session - close(); + close(SessionCloseErrorCode::INVALID_MESSAGE); return; } @@ -104,14 +104,14 @@ namespace net std::chrono::steady_clock::now() - m_connection_time ).count() > KI_CONNECTION_TIMEOUT) { - close(); + close(SessionCloseErrorCode::SESSION_OFFER_TIMED_OUT); return; } // Make sure they're accepting this session if (accept.get_session_id() != m_id) { - close(); + close(SessionCloseErrorCode::INVALID_MESSAGE); return; } @@ -134,7 +134,7 @@ namespace net { // The KEEP_ALIVE wasn't valid... // Close the session - close(); + close(SessionCloseErrorCode::INVALID_MESSAGE); return; } @@ -156,7 +156,7 @@ namespace net { // The KEEP_ALIVE_RSP wasn't valid... // Close the session - close(); + close(SessionCloseErrorCode::INVALID_MESSAGE); return; } diff --git a/src/protocol/net/Session.cpp b/src/protocol/net/Session.cpp index 6a995c8..9807773 100644 --- a/src/protocol/net/Session.cpp +++ b/src/protocol/net/Session.cpp @@ -104,7 +104,7 @@ namespace net // correctly. if (m_start_signal != KI_START_SIGNAL) { - close(); + close(SessionCloseErrorCode::INVALID_FRAMING_START_SIGNAL); return; } @@ -126,7 +126,7 @@ namespace net // stop processing data. if (m_incoming_packet_size > m_maximum_packet_size) { - close(); + close(SessionCloseErrorCode::INVALID_FRAMING_SIZE_EXCEEDS_MAXIMUM); return; } From a00f9b500329d95d9b63fe7f2c0227175ce91f26 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Fri, 20 Apr 2018 18:57:05 +0100 Subject: [PATCH 47/49] net: Pass a pointer rather than a reference in DMLSession::on_message --- include/ki/protocol/net/DMLSession.h | 2 +- src/protocol/net/DMLSession.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ki/protocol/net/DMLSession.h b/include/ki/protocol/net/DMLSession.h index 345b563..29bebbe 100644 --- a/include/ki/protocol/net/DMLSession.h +++ b/include/ki/protocol/net/DMLSession.h @@ -29,7 +29,7 @@ namespace net void send_message(const dml::Message &message); protected: void on_application_message(const PacketHeader& header) override; - virtual void on_message(const dml::Message &message) {} + virtual void on_message(const dml::Message *message) {} virtual void on_invalid_message(InvalidDMLMessageErrorCode error) {} private: const dml::MessageManager &m_manager; diff --git a/src/protocol/net/DMLSession.cpp b/src/protocol/net/DMLSession.cpp index aec5c48..22394f2 100644 --- a/src/protocol/net/DMLSession.cpp +++ b/src/protocol/net/DMLSession.cpp @@ -31,7 +31,7 @@ namespace net // Are we sufficiently authenticated to handle this message? if (get_access_level() >= message->get_access_level()) - on_message(*message); + on_message(message); else on_invalid_message(InvalidDMLMessageErrorCode::INSUFFICIENT_ACCESS); delete message; From 6d02bfe06a663c145624ef060064f73e427089b4 Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Sun, 22 Apr 2018 17:11:12 +0100 Subject: [PATCH 48/49] protocol: Give exceptions their own error codes --- include/ki/protocol/control/ClientKeepAlive.h | 1 + include/ki/protocol/control/ServerKeepAlive.h | 1 + include/ki/protocol/exception.h | 44 +++++++++++++-- include/ki/protocol/net/DMLSession.h | 5 ++ include/ki/protocol/net/PacketHeader.h | 1 + src/protocol/control/ClientKeepAlive.cpp | 2 +- src/protocol/control/ServerKeepAlive.cpp | 2 +- src/protocol/control/SessionAccept.cpp | 2 +- src/protocol/control/SessionOffer.cpp | 2 +- src/protocol/dml/Message.cpp | 13 +++-- src/protocol/dml/MessageHeader.cpp | 2 +- src/protocol/dml/MessageManager.cpp | 53 +++++++++++-------- src/protocol/dml/MessageModule.cpp | 6 +-- src/protocol/net/DMLSession.cpp | 40 +++++++++++++- src/protocol/net/PacketHeader.cpp | 9 ++-- 15 files changed, 139 insertions(+), 44 deletions(-) diff --git a/include/ki/protocol/control/ClientKeepAlive.h b/include/ki/protocol/control/ClientKeepAlive.h index 802a1bd..5b9f9bb 100644 --- a/include/ki/protocol/control/ClientKeepAlive.h +++ b/include/ki/protocol/control/ClientKeepAlive.h @@ -1,6 +1,7 @@ #pragma once #include "../../util/Serializable.h" #include +#include namespace ki { diff --git a/include/ki/protocol/control/ServerKeepAlive.h b/include/ki/protocol/control/ServerKeepAlive.h index c7ad516..520ff51 100644 --- a/include/ki/protocol/control/ServerKeepAlive.h +++ b/include/ki/protocol/control/ServerKeepAlive.h @@ -1,6 +1,7 @@ #pragma once #include "../../util/Serializable.h" #include +#include namespace ki { diff --git a/include/ki/protocol/exception.h b/include/ki/protocol/exception.h index 998c011..b379ef6 100644 --- a/include/ki/protocol/exception.h +++ b/include/ki/protocol/exception.h @@ -8,19 +8,57 @@ namespace protocol class runtime_error : public std::runtime_error { public: - runtime_error(std::string message) : std::runtime_error(message) { } + explicit runtime_error(std::string message) : std::runtime_error(message) {} }; class parse_error : public runtime_error { public: - parse_error(std::string message) : runtime_error(message) { } + enum code + { + NONE, + INVALID_XML_DATA, + INVALID_HEADER_DATA, + INSUFFICIENT_MESSAGE_DATA, + INVALID_MESSAGE_DATA + }; + + explicit parse_error(std::string message, code error = code::NONE) + : runtime_error(message) + { + m_code = error; + } + + code get_error_code() const { return m_code; } + private: + code m_code; }; class value_error : public runtime_error { public: - value_error(std::string message) : runtime_error(message) { } + enum code + { + NONE, + MISSING_FILE, + OVERWRITES_LOOKUP, + EXCEEDS_LIMIT, + + DML_INVALID_SERVICE, + DML_INVALID_PROTOCOL_TYPE, + DML_INVALID_MESSAGE_TYPE, + DML_INVALID_MESSAGE_NAME + }; + + explicit value_error(std::string message, code error = code::NONE) + : runtime_error(message) + { + m_code = error; + } + + code get_error_code() const { return m_code; } + private: + code m_code; }; } } \ No newline at end of file diff --git a/include/ki/protocol/net/DMLSession.h b/include/ki/protocol/net/DMLSession.h index 29bebbe..c86e29e 100644 --- a/include/ki/protocol/net/DMLSession.h +++ b/include/ki/protocol/net/DMLSession.h @@ -10,7 +10,12 @@ namespace net { enum class InvalidDMLMessageErrorCode { + NONE, + UNKNOWN, + INVALID_HEADER_DATA, INVALID_MESSAGE_DATA, + INVALID_SERVICE, + INVALID_MESSAGE_TYPE, INSUFFICIENT_ACCESS }; diff --git a/include/ki/protocol/net/PacketHeader.h b/include/ki/protocol/net/PacketHeader.h index 702cb3b..e516fa9 100644 --- a/include/ki/protocol/net/PacketHeader.h +++ b/include/ki/protocol/net/PacketHeader.h @@ -1,6 +1,7 @@ #pragma once #include "../../util/Serializable.h" #include +#include namespace ki { diff --git a/src/protocol/control/ClientKeepAlive.cpp b/src/protocol/control/ClientKeepAlive.cpp index 0077fec..49303e4 100644 --- a/src/protocol/control/ClientKeepAlive.cpp +++ b/src/protocol/control/ClientKeepAlive.cpp @@ -69,7 +69,7 @@ namespace control { std::ostringstream oss; oss << "Error reading ClientKeepAlive payload: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA); } m_session_id = session_id->get_value(); diff --git a/src/protocol/control/ServerKeepAlive.cpp b/src/protocol/control/ServerKeepAlive.cpp index c967921..4b77df5 100644 --- a/src/protocol/control/ServerKeepAlive.cpp +++ b/src/protocol/control/ServerKeepAlive.cpp @@ -45,7 +45,7 @@ namespace control { std::ostringstream oss; oss << "Error reading ServerKeepAlive payload: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA); } m_timestamp = timestamp->get_value(); diff --git a/src/protocol/control/SessionAccept.cpp b/src/protocol/control/SessionAccept.cpp index 26bd526..20c92ca 100644 --- a/src/protocol/control/SessionAccept.cpp +++ b/src/protocol/control/SessionAccept.cpp @@ -73,7 +73,7 @@ namespace control { std::ostringstream oss; oss << "Error reading SessionAccept payload: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA); } m_timestamp = timestamp->get_value(); diff --git a/src/protocol/control/SessionOffer.cpp b/src/protocol/control/SessionOffer.cpp index 4ca2635..07627eb 100644 --- a/src/protocol/control/SessionOffer.cpp +++ b/src/protocol/control/SessionOffer.cpp @@ -71,7 +71,7 @@ namespace control { std::ostringstream oss; oss << "Error reading SessionOffer payload: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA); } m_session_id = session_id->get_value(); diff --git a/src/protocol/dml/Message.cpp b/src/protocol/dml/Message.cpp index 27d4110..215a694 100644 --- a/src/protocol/dml/Message.cpp +++ b/src/protocol/dml/Message.cpp @@ -50,7 +50,7 @@ namespace dml std::ostringstream oss; oss << "Error reading DML message payload: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA); } } } @@ -140,10 +140,12 @@ namespace dml { // Check for mismatches between the header and template if (m_header.get_service_id() != m_template->get_service_id()) - throw value_error("ServiceID mismatch between MessageHeader and assigned template."); + throw value_error("ServiceID mismatch between MessageHeader and assigned template.", + value_error::DML_INVALID_SERVICE); if (m_header.get_type() != m_template->get_type()) - throw value_error("Message Type mismatch between MessageHeader and assigned template."); - + throw value_error("Message Type mismatch between MessageHeader and assigned template.", + value_error::DML_INVALID_MESSAGE_TYPE); + // Read the payload into the record m_record->read_from(istream); } @@ -155,7 +157,8 @@ namespace dml m_raw_data.resize(size); istream.read(m_raw_data.data(), size); if (istream.fail()) - throw parse_error("Not enough data was available to read DML message payload."); + throw parse_error("Not enough data was available to read DML message payload.", + parse_error::INSUFFICIENT_MESSAGE_DATA); } } diff --git a/src/protocol/dml/MessageHeader.cpp b/src/protocol/dml/MessageHeader.cpp index 46290a3..0e4d746 100644 --- a/src/protocol/dml/MessageHeader.cpp +++ b/src/protocol/dml/MessageHeader.cpp @@ -70,7 +70,7 @@ namespace dml { std::ostringstream oss; oss << "Error reading MessageHeader: " << e.what(); - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_HEADER_DATA); } m_service_id = service_id->get_value(); diff --git a/src/protocol/dml/MessageManager.cpp b/src/protocol/dml/MessageManager.cpp index 328e597..09b105d 100644 --- a/src/protocol/dml/MessageManager.cpp +++ b/src/protocol/dml/MessageManager.cpp @@ -31,7 +31,7 @@ namespace dml { std::ostringstream oss; oss << "Could not open file: " << filepath; - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::MISSING_FILE); } // Load contents into memory @@ -52,7 +52,7 @@ namespace dml std::ostringstream oss; oss << "Failed to parse: " << filepath; - throw parse_error(oss.str()); + throw parse_error(oss.str(), parse_error::INVALID_XML_DATA); } // It's safe to allocate the module we're working on now @@ -118,8 +118,8 @@ namespace dml std::ostringstream oss; oss << "Message Module has already been loaded with Service ID "; - oss << message_module->get_service_id(); - throw value_error(oss.str()); + oss << (uint16_t)message_module->get_service_id(); + throw value_error(oss.str(), value_error::OVERWRITES_LOOKUP); } if (m_protocol_type_map.count(message_module->get_protocol_type()) == 1) @@ -130,7 +130,7 @@ namespace dml std::ostringstream oss; oss << "Message Module has already been loaded with Protocol Type "; oss << message_module->get_protocol_type(); - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::OVERWRITES_LOOKUP); } // Add it to our maps @@ -162,8 +162,8 @@ namespace dml if (!message_module) { std::ostringstream oss; - oss << "No service exists with id: " << service_id; - throw value_error(oss.str()); + oss << "No service exists with id: " << (uint16_t)service_id; + throw value_error(oss.str(), value_error::DML_INVALID_SERVICE); } return message_module->create_message(message_type); @@ -175,8 +175,8 @@ namespace dml if (!message_module) { std::ostringstream oss; - oss << "No service exists with id: " << service_id; - throw value_error(oss.str()); + oss << "No service exists with id: " << (uint16_t)service_id; + throw value_error(oss.str(), value_error::DML_INVALID_SERVICE); } return message_module->create_message(message_name); @@ -189,7 +189,7 @@ namespace dml { std::ostringstream oss; oss << "No service exists with protocol type: " << protocol_type; - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::DML_INVALID_PROTOCOL_TYPE); } return message_module->create_message(message_type); @@ -202,7 +202,7 @@ namespace dml { std::ostringstream oss; oss << "No service exists with protocol type: " << protocol_type; - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::DML_INVALID_PROTOCOL_TYPE); } return message_module->create_message(message_name); @@ -212,28 +212,35 @@ namespace dml { // Read the message header MessageHeader header; - try - { - header.read_from(istream); - } - catch (parse_error &e) - { - return nullptr; - } + header.read_from(istream); // Get the message module that uses the specified service id auto *message_module = get_module(header.get_service_id()); if (!message_module) - return nullptr; + { + std::ostringstream oss; + oss << "No service exists with id: " << (uint16_t)header.get_service_id(); + throw value_error(oss.str(), value_error::DML_INVALID_SERVICE); + } // Get the message template for this message type auto *message_template = message_module->get_message_template(header.get_type()); if (!message_template) - return nullptr; + { + std::ostringstream oss; + oss << "No message exists with type: " << (uint16_t)header.get_service_id(); + oss << "(service=" << message_module->get_protocol_type() << ")"; + throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_TYPE); + } // Make sure that the size specified is enough to read this message if (header.get_message_size() < message_template->get_record().get_size()) - return nullptr; + { + std::ostringstream oss; + oss << "No message exists with type: " << (uint16_t)header.get_service_id(); + oss << "(service=" << message_module->get_protocol_type() << ")"; + throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_TYPE); + } // Create a new Message from the template auto *message = new Message(message_template); @@ -244,7 +251,7 @@ namespace dml catch (ki::dml::parse_error &e) { delete message; - return nullptr; + throw parse_error("Failed to read DML message payload.", parse_error::INVALID_MESSAGE_DATA); } return message; } diff --git a/src/protocol/dml/MessageModule.cpp b/src/protocol/dml/MessageModule.cpp index 11a48c0..147c295 100644 --- a/src/protocol/dml/MessageModule.cpp +++ b/src/protocol/dml/MessageModule.cpp @@ -135,7 +135,7 @@ namespace dml // Make sure we haven't overflowed if (message_type == 0) - throw value_error("Module has more than 254 messages."); + throw value_error("Module has more than 254 messages.", value_error::EXCEEDS_LIMIT); } } @@ -147,7 +147,7 @@ namespace dml std::ostringstream oss; oss << "No message exists with type: " << message_type; oss << "(service=" << m_protocol_type << ")"; - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_TYPE); } return message_template->create_message(); @@ -161,7 +161,7 @@ namespace dml std::ostringstream oss; oss << "No message exists with name: " << message_name; oss << "(service=" << m_protocol_type << ")"; - throw value_error(oss.str()); + throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_NAME); } return message_template->create_message(); diff --git a/src/protocol/net/DMLSession.cpp b/src/protocol/net/DMLSession.cpp index 22394f2..06d75d7 100644 --- a/src/protocol/net/DMLSession.cpp +++ b/src/protocol/net/DMLSession.cpp @@ -1,4 +1,5 @@ #include "ki/protocol/net/DMLSession.h" +#include "ki/protocol/exception.h" namespace ki { @@ -22,10 +23,45 @@ namespace net void DMLSession::on_application_message(const PacketHeader& header) { // Attempt to create a Message instance from the data in the stream - const auto *message = m_manager.message_from_binary(m_data_stream); + auto error_code = InvalidDMLMessageErrorCode::NONE; + const dml::Message *message = nullptr; + try + { + message = m_manager.message_from_binary(m_data_stream); + } + catch (parse_error &e) + { + switch (e.get_error_code()) + { + case parse_error::INVALID_HEADER_DATA: + error_code = InvalidDMLMessageErrorCode::INVALID_HEADER_DATA; + break; + case parse_error::INSUFFICIENT_MESSAGE_DATA: + case parse_error::INVALID_MESSAGE_DATA: + error_code = InvalidDMLMessageErrorCode::INVALID_MESSAGE_DATA; + break; + default: + error_code = InvalidDMLMessageErrorCode::UNKNOWN; + } + } + catch (value_error &e) + { + switch (e.get_error_code()) + { + case value_error::DML_INVALID_SERVICE: + error_code = InvalidDMLMessageErrorCode::INVALID_SERVICE; + break; + case value_error::DML_INVALID_MESSAGE_TYPE: + error_code = InvalidDMLMessageErrorCode::INVALID_MESSAGE_TYPE; + break; + default: + error_code = InvalidDMLMessageErrorCode::UNKNOWN; + } + } + if (!message) { - on_invalid_message(InvalidDMLMessageErrorCode::INVALID_MESSAGE_DATA); + on_invalid_message(error_code); return; } diff --git a/src/protocol/net/PacketHeader.cpp b/src/protocol/net/PacketHeader.cpp index e1091f8..986087e 100644 --- a/src/protocol/net/PacketHeader.cpp +++ b/src/protocol/net/PacketHeader.cpp @@ -46,13 +46,16 @@ namespace net { m_control = istream.get() >= 1; if (istream.fail()) - throw parse_error("Not enough data was available to read packet header. (m_control)"); + throw parse_error("Not enough data was available to read packet header. (m_control)", + parse_error::INVALID_HEADER_DATA); m_opcode = istream.get(); if (istream.fail()) - throw parse_error("Not enough data was available to read packet header. (m_opcode)"); + throw parse_error("Not enough data was available to read packet header. (m_opcode)", + parse_error::INVALID_HEADER_DATA); istream.ignore(2); if (istream.eof()) - throw parse_error("Not enough data was available to read packet header. (ignored bytes)"); + throw parse_error("Not enough data was available to read packet header. (ignored bytes)", + parse_error::INVALID_HEADER_DATA); } size_t PacketHeader::get_size() const From 05e48a2cbbf2e48e1debe1e40835f9e8505eef8a Mon Sep 17 00:00:00 2001 From: Joshua Scott Date: Tue, 24 Apr 2018 20:06:53 +0100 Subject: [PATCH 49/49] net: Fix is_alive returning false by giving some leniency --- src/protocol/net/ClientSession.cpp | 4 ++-- src/protocol/net/ServerSession.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/protocol/net/ClientSession.cpp b/src/protocol/net/ClientSession.cpp index 9232a5a..b77506d 100644 --- a/src/protocol/net/ClientSession.cpp +++ b/src/protocol/net/ClientSession.cpp @@ -42,12 +42,12 @@ namespace net if (!m_established) return std::chrono::duration_cast( std::chrono::steady_clock::now() - m_creation_time - ).count() <= KI_CONNECTION_TIMEOUT; + ).count() <= (KI_CONNECTION_TIMEOUT * 2); // Otherwise, use the last time we received a heartbeat. return std::chrono::duration_cast( std::chrono::steady_clock::now() - m_last_received_heartbeat_time - ).count() <= KI_SERVER_HEARTBEAT; + ).count() <= (KI_SERVER_HEARTBEAT * 2); } void ClientSession::on_connected() diff --git a/src/protocol/net/ServerSession.cpp b/src/protocol/net/ServerSession.cpp index 72efe98..dc55b66 100644 --- a/src/protocol/net/ServerSession.cpp +++ b/src/protocol/net/ServerSession.cpp @@ -34,12 +34,12 @@ namespace net if (!m_established) return std::chrono::duration_cast( std::chrono::steady_clock::now() - m_creation_time - ).count() <= KI_CONNECTION_TIMEOUT; + ).count() <= (KI_CONNECTION_TIMEOUT * 2); // Otherwise, use the last time we received a heartbeat. return std::chrono::duration_cast( std::chrono::steady_clock::now() - m_last_received_heartbeat_time - ).count() <= KI_CLIENT_HEARTBEAT; + ).count() <= (KI_CLIENT_HEARTBEAT * 2); } void ServerSession::on_connected()