From 02c093362f5bf9705281746846ea150ee8546686 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Wed, 22 Mar 2023 20:22:56 +0100 Subject: [PATCH 001/203] Implement ACKMessage #2 --- Server/ACKMessage.h | 10 +++--- Server/Snowflake.h | 8 +++++ ServerUnitTest/ParseMessageUnitTests.cpp | 36 +++++++++++++++++++ ...ests.cpp => SerializeMessageUnitTests.cpp} | 0 ServerUnitTest/ServerUnitTest.vcxproj | 3 +- ServerUnitTest/ServerUnitTest.vcxproj.filters | 5 ++- 6 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 ServerUnitTest/ParseMessageUnitTests.cpp rename ServerUnitTest/{MessageUnitTests.cpp => SerializeMessageUnitTests.cpp} (100%) diff --git a/Server/ACKMessage.h b/Server/ACKMessage.h index aae5537..6d29295 100644 --- a/Server/ACKMessage.h +++ b/Server/ACKMessage.h @@ -18,7 +18,7 @@ namespace SocketMessages } explicit AcknowledgeMessage(Snowflake const& id) - : m_messageId(id) + : m_messageId{ id } { header.messageType = MessageType::AcknowledgeMessage; header.bodySize = GetBodySize(); @@ -29,7 +29,9 @@ namespace SocketMessages if (buffer.size() != GetBodySize()) return false; - std::memcpy(std::bit_cast(&m_messageId), std::bit_cast(buffer.data()), sizeof(Snowflake::Value)); + uint64_t id{}; + std::memcpy(std::bit_cast(&id), std::bit_cast(buffer.data()), sizeof(id)); + m_messageId = Snowflake(id); return true; } @@ -39,8 +41,8 @@ namespace SocketMessages std::vector headerBuffer = header.Serialize(); std::memcpy(&buffer[0], &headerBuffer[0], MessageHeader::size); - uint64_t id = m_messageId; - std::memcpy(&buffer[SocketMessages::MessageHeader::size], &id, sizeof(id)); + auto id = static_cast(m_messageId); + std::memcpy(&buffer[MessageHeader::size], &id, sizeof(id)); return buffer; } diff --git a/Server/Snowflake.h b/Server/Snowflake.h index 15d51d7..67725a2 100644 --- a/Server/Snowflake.h +++ b/Server/Snowflake.h @@ -19,6 +19,14 @@ struct Snowflake Value.sequence = 0; } + explicit Snowflake(uint64_t const& id) + { + Value.timestamp = id >> 22; + Value.workerId = (id >> 17) & 0x1F; + Value.processId = (id >> 12) & 0x1F; + Value.sequence = id & 0xFFF; + } + struct { uint64_t timestamp : 42; diff --git a/ServerUnitTest/ParseMessageUnitTests.cpp b/ServerUnitTest/ParseMessageUnitTests.cpp new file mode 100644 index 0000000..9d9e400 --- /dev/null +++ b/ServerUnitTest/ParseMessageUnitTests.cpp @@ -0,0 +1,36 @@ +#include "CppUnitTest.h" + +#include +#include + +#include "Snowflake.h" +#include "SocketMessages.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace ServerUnitTest +{ + TEST_CLASS(ParseMessages) + { + public: + + TEST_METHOD(ParseACKMessage) + { + Snowflake messageId{}; + + SocketMessages::AcknowledgeMessage ackMessage{ messageId }; + + std::vector messageBuffer = ackMessage.Serialize(); + std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); + + Assert::IsFalse(ackMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); + Assert::IsTrue(ackMessage.ParseBody(buffer), L"Valid data can't be parsed"); + + Assert::AreEqual( + static_cast(messageId), + static_cast(ackMessage.GetAcknowledgedMessageId()), + L"Acknowledged message Id id wrong" + ); + } + }; +} diff --git a/ServerUnitTest/MessageUnitTests.cpp b/ServerUnitTest/SerializeMessageUnitTests.cpp similarity index 100% rename from ServerUnitTest/MessageUnitTests.cpp rename to ServerUnitTest/SerializeMessageUnitTests.cpp diff --git a/ServerUnitTest/ServerUnitTest.vcxproj b/ServerUnitTest/ServerUnitTest.vcxproj index 659b7c1..7d66719 100644 --- a/ServerUnitTest/ServerUnitTest.vcxproj +++ b/ServerUnitTest/ServerUnitTest.vcxproj @@ -168,7 +168,8 @@ - + + diff --git a/ServerUnitTest/ServerUnitTest.vcxproj.filters b/ServerUnitTest/ServerUnitTest.vcxproj.filters index af8ba66..6a76d6d 100644 --- a/ServerUnitTest/ServerUnitTest.vcxproj.filters +++ b/ServerUnitTest/ServerUnitTest.vcxproj.filters @@ -15,7 +15,10 @@ - + + Source Files + + Source Files From de6cceaf86b2470eb692482c6dee3eb512293942 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Wed, 22 Mar 2023 20:34:48 +0100 Subject: [PATCH 002/203] Implement ErrorMessage #2 --- Server/ErrorMessage.h | 2 ++ ServerUnitTest/ParseMessageUnitTests.cpp | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/Server/ErrorMessage.h b/Server/ErrorMessage.h index 81a5557..2b7b4e1 100644 --- a/Server/ErrorMessage.h +++ b/Server/ErrorMessage.h @@ -38,6 +38,8 @@ namespace SocketMessages if (buffer.size() != GetBodySize()) return false; + std::memcpy(std::bit_cast(&m_errorCode), &buffer[0], sizeof(m_errorCode)); + return true; } diff --git a/ServerUnitTest/ParseMessageUnitTests.cpp b/ServerUnitTest/ParseMessageUnitTests.cpp index 9d9e400..fe6cbe2 100644 --- a/ServerUnitTest/ParseMessageUnitTests.cpp +++ b/ServerUnitTest/ParseMessageUnitTests.cpp @@ -32,5 +32,24 @@ namespace ServerUnitTest L"Acknowledged message Id id wrong" ); } + + TEST_METHOD(ParseErrorMessage) + { + SocketMessages::ErrorCode errorCode{ SocketMessages::ErrorCode::InvalidMessage }; + + SocketMessages::ErrorMessage errorMessage{ errorCode }; + + std::vector messageBuffer = errorMessage.Serialize(); + std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); + + Assert::IsFalse(errorMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); + Assert::IsTrue(errorMessage.ParseBody(buffer), L"Valid data can't be parsed"); + + Assert::AreEqual( + static_cast(errorCode), + static_cast(errorMessage.GetErrorCode()), + L"Error code is wrong" + ); + } }; } From e498d21b1ec6be8bec842fb4e06593081362188c Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Wed, 22 Mar 2023 20:42:04 +0100 Subject: [PATCH 003/203] Implement HelloMessage unit test #2 --- ServerUnitTest/ParseMessageUnitTests.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ServerUnitTest/ParseMessageUnitTests.cpp b/ServerUnitTest/ParseMessageUnitTests.cpp index fe6cbe2..47c2731 100644 --- a/ServerUnitTest/ParseMessageUnitTests.cpp +++ b/ServerUnitTest/ParseMessageUnitTests.cpp @@ -51,5 +51,22 @@ namespace ServerUnitTest L"Error code is wrong" ); } + + TEST_METHOD(ParseHelloMessage) + { + SocketMessages::HelloMessage helloMessage{}; + + std::vector messageBuffer = helloMessage.Serialize(); + std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); + + Assert::IsFalse(helloMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); + Assert::IsTrue(helloMessage.ParseBody(buffer), L"Valid data can't be parsed"); + + Assert::AreEqual( + static_cast(CurrentVersion), + static_cast(helloMessage.GetVersion()), + L"API version is wrong" + ); + } }; } From 9ee3dd207f976907382cd1b511f8a86d9705ef10 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Wed, 22 Mar 2023 20:48:18 +0100 Subject: [PATCH 004/203] Implement IdentifyMessage #2 --- Server/IdentifyMessage.h | 3 +++ ServerUnitTest/ParseMessageUnitTests.cpp | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/Server/IdentifyMessage.h b/Server/IdentifyMessage.h index b496c8a..6dd348c 100644 --- a/Server/IdentifyMessage.h +++ b/Server/IdentifyMessage.h @@ -22,6 +22,9 @@ namespace SocketMessages if (buffer.size() != GetBodySize()) return false; + std::memcpy(std::bit_cast(&m_usernameLength), std::bit_cast(buffer.data()), sizeof(m_usernameLength)); + m_username = std::string(buffer.begin() + sizeof(m_usernameLength), buffer.end()); + return true; } diff --git a/ServerUnitTest/ParseMessageUnitTests.cpp b/ServerUnitTest/ParseMessageUnitTests.cpp index 47c2731..4cf27da 100644 --- a/ServerUnitTest/ParseMessageUnitTests.cpp +++ b/ServerUnitTest/ParseMessageUnitTests.cpp @@ -68,5 +68,22 @@ namespace ServerUnitTest L"API version is wrong" ); } + + TEST_METHOD(ParseIdentifyMessage) + { + std::string username = "Username"; + + SocketMessages::IdentifyMessage identifyMessage{}; + + identifyMessage.SetUsername(username); + + std::vector messageBuffer = identifyMessage.Serialize(); + std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); + + Assert::IsFalse(identifyMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); + Assert::IsTrue(identifyMessage.ParseBody(buffer), L"Valid data can't be parsed"); + + Assert::AreEqual(username, identifyMessage.GetUsername(), L"Username is wrong"); + } }; } From eac395db8ea7071c67a390230f84a0d6bdf56627 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Wed, 22 Mar 2023 20:49:39 +0100 Subject: [PATCH 005/203] Implement KeepAliveMessage unit test --- ServerUnitTest/ParseMessageUnitTests.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ServerUnitTest/ParseMessageUnitTests.cpp b/ServerUnitTest/ParseMessageUnitTests.cpp index 4cf27da..888a570 100644 --- a/ServerUnitTest/ParseMessageUnitTests.cpp +++ b/ServerUnitTest/ParseMessageUnitTests.cpp @@ -85,5 +85,16 @@ namespace ServerUnitTest Assert::AreEqual(username, identifyMessage.GetUsername(), L"Username is wrong"); } + + TEST_METHOD(ParseKeepAliveMessage) + { + SocketMessages::KeepAliveMessage keepAliveMessage{}; + + std::vector messageBuffer = keepAliveMessage.Serialize(); + std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); + + Assert::IsFalse(keepAliveMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); + Assert::IsTrue(keepAliveMessage.ParseBody(buffer), L"Valid data can't be parsed"); + } }; } From eca88f8be802bc48edc702383792a10ec062f2ed Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Wed, 22 Mar 2023 20:49:39 +0100 Subject: [PATCH 006/203] Implement KeepAliveMessage unit test #2 --- ServerUnitTest/ParseMessageUnitTests.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ServerUnitTest/ParseMessageUnitTests.cpp b/ServerUnitTest/ParseMessageUnitTests.cpp index 4cf27da..888a570 100644 --- a/ServerUnitTest/ParseMessageUnitTests.cpp +++ b/ServerUnitTest/ParseMessageUnitTests.cpp @@ -85,5 +85,16 @@ namespace ServerUnitTest Assert::AreEqual(username, identifyMessage.GetUsername(), L"Username is wrong"); } + + TEST_METHOD(ParseKeepAliveMessage) + { + SocketMessages::KeepAliveMessage keepAliveMessage{}; + + std::vector messageBuffer = keepAliveMessage.Serialize(); + std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); + + Assert::IsFalse(keepAliveMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); + Assert::IsTrue(keepAliveMessage.ParseBody(buffer), L"Valid data can't be parsed"); + } }; } From 25a2d3571e1254b1c0f51509326c0aa00a7f8c5a Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Wed, 22 Mar 2023 21:06:51 +0100 Subject: [PATCH 007/203] Implement ReceiveChatMessage #2 --- Server/ReceiveChatMessage.h | 22 ++++++++++++++++++++++ ServerUnitTest/ParseMessageUnitTests.cpp | 19 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/Server/ReceiveChatMessage.h b/Server/ReceiveChatMessage.h index 6f51901..bd19fb4 100644 --- a/Server/ReceiveChatMessage.h +++ b/Server/ReceiveChatMessage.h @@ -21,6 +21,28 @@ namespace SocketMessages if (buffer.size() != GetBodySize()) return false; + std::memcpy( + std::bit_cast(&m_authorUsernameLength), + buffer.data(), + sizeof(m_authorUsernameLength) + ); + m_authorUsername = std::string( + buffer.begin() + sizeof(m_authorUsernameLength), + buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength + ); + + std::memcpy( + std::bit_cast(&m_chatMessageLength), + buffer.data() + sizeof(m_authorUsernameLength) + m_authorUsernameLength, + sizeof(m_chatMessageLength) + ); + m_chatMessage = std::string( + buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength), + buffer.end() + ); + + header.bodySize = GetBodySize(); + return true; } diff --git a/ServerUnitTest/ParseMessageUnitTests.cpp b/ServerUnitTest/ParseMessageUnitTests.cpp index 888a570..5bd2bb1 100644 --- a/ServerUnitTest/ParseMessageUnitTests.cpp +++ b/ServerUnitTest/ParseMessageUnitTests.cpp @@ -96,5 +96,24 @@ namespace ServerUnitTest Assert::IsFalse(keepAliveMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); Assert::IsTrue(keepAliveMessage.ParseBody(buffer), L"Valid data can't be parsed"); } + + TEST_METHOD(ParseReceiveChatMessage) + { + std::string authorUsername = "Username"; + std::string chatMessage = "Message"; + + SocketMessages::ReceiveChatMessage receiveChatMessage{}; + receiveChatMessage.SetAuthorUsername(authorUsername); + receiveChatMessage.SetChatMessage(chatMessage); + + std::vector messageBuffer = receiveChatMessage.Serialize(); + std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); + + Assert::IsFalse(receiveChatMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); + Assert::IsTrue(receiveChatMessage.ParseBody(buffer), L"Valid data can't be parsed"); + + Assert::AreEqual(authorUsername, receiveChatMessage.GetAuthorUsername(), L"Author username is wrong"); + Assert::AreEqual(chatMessage, receiveChatMessage.GetChatMessage(), L"Chat message is wrong"); + } }; } From 797db0ed03a289e60a96081b300754a9ed00614e Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Wed, 22 Mar 2023 21:08:17 +0100 Subject: [PATCH 008/203] Increase readability --- Server/ACKMessage.h | 1 + Server/HelloMessage.h | 1 + Server/IdentifyMessage.h | 2 ++ 3 files changed, 4 insertions(+) diff --git a/Server/ACKMessage.h b/Server/ACKMessage.h index 6d29295..5f6e4e1 100644 --- a/Server/ACKMessage.h +++ b/Server/ACKMessage.h @@ -32,6 +32,7 @@ namespace SocketMessages uint64_t id{}; std::memcpy(std::bit_cast(&id), std::bit_cast(buffer.data()), sizeof(id)); m_messageId = Snowflake(id); + return true; } diff --git a/Server/HelloMessage.h b/Server/HelloMessage.h index 65d9d7c..2f0fb8c 100644 --- a/Server/HelloMessage.h +++ b/Server/HelloMessage.h @@ -22,6 +22,7 @@ namespace SocketMessages return false; std::memcpy(std::bit_cast(&m_version), std::bit_cast(buffer.data()), sizeof(m_version)); + return true; } diff --git a/Server/IdentifyMessage.h b/Server/IdentifyMessage.h index 6dd348c..b9caf03 100644 --- a/Server/IdentifyMessage.h +++ b/Server/IdentifyMessage.h @@ -25,6 +25,8 @@ namespace SocketMessages std::memcpy(std::bit_cast(&m_usernameLength), std::bit_cast(buffer.data()), sizeof(m_usernameLength)); m_username = std::string(buffer.begin() + sizeof(m_usernameLength), buffer.end()); + header.bodySize = GetBodySize(); + return true; } From 59cc8c01b7f183a4c6f2c0b86328614bc6e5acbd Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Wed, 22 Mar 2023 21:12:19 +0100 Subject: [PATCH 009/203] Implement SendChatMessage #2 --- Server/SendChatMessage.h | 29 +++++++------------- ServerUnitTest/ParseMessageUnitTests.cpp | 16 +++++++++++ ServerUnitTest/SerializeMessageUnitTests.cpp | 8 ------ 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/Server/SendChatMessage.h b/Server/SendChatMessage.h index b39599f..890ceb3 100644 --- a/Server/SendChatMessage.h +++ b/Server/SendChatMessage.h @@ -23,6 +23,13 @@ namespace SocketMessages if (buffer.size() != GetBodySize()) return false; + std::memcpy(std::bit_cast(&m_chatMessageLength), + buffer.data(), sizeof(m_chatMessageLength)); + m_chatMessage = std::string(buffer.begin() + sizeof(m_chatMessageLength), + buffer.begin() + sizeof(m_chatMessageLength) + m_chatMessageLength); + + header.bodySize = GetBodySize(); + return true; } @@ -30,9 +37,9 @@ namespace SocketMessages { std::vector buffer(MessageHeader::size + GetBodySize(), 0); std::vector headerBuffer = header.Serialize(); - + uint8_t cursor = 0; - + std::memcpy(&buffer[cursor], &headerBuffer[0], MessageHeader::size); cursor += MessageHeader::size; std::memcpy(&buffer[cursor], &m_chatMessageLength, sizeof(m_chatMessageLength)); @@ -44,21 +51,7 @@ namespace SocketMessages uint64_t constexpr GetBodySize() const final { - return sizeof(m_authorUsenameLength) + m_authorUsenameLength + sizeof(m_chatMessageLength) + m_chatMessageLength; - } - - [[nodiscard]] constexpr std::string const& GetAuthorUsername() const { return m_authorUsername; } - constexpr bool SetAuthorUsername(std::string_view const& authorUsername) - { - if (authorUsername.size() > UsernameMaxLength || authorUsername.size() < UsernameMinLength) - return false; - - m_authorUsername = authorUsername; - m_authorUsenameLength = static_cast(authorUsername.size()); - - header.bodySize = GetBodySize(); - - return true; + return sizeof(m_chatMessageLength) + m_chatMessageLength; } [[nodiscard]] constexpr std::string const& GetChatMessage() const { return m_chatMessage; } @@ -76,8 +69,6 @@ namespace SocketMessages } private: - uint8_t m_authorUsenameLength{}; - std::string m_authorUsername{}; uint16_t m_chatMessageLength{}; std::string m_chatMessage{}; }; diff --git a/ServerUnitTest/ParseMessageUnitTests.cpp b/ServerUnitTest/ParseMessageUnitTests.cpp index 5bd2bb1..6ccf581 100644 --- a/ServerUnitTest/ParseMessageUnitTests.cpp +++ b/ServerUnitTest/ParseMessageUnitTests.cpp @@ -115,5 +115,21 @@ namespace ServerUnitTest Assert::AreEqual(authorUsername, receiveChatMessage.GetAuthorUsername(), L"Author username is wrong"); Assert::AreEqual(chatMessage, receiveChatMessage.GetChatMessage(), L"Chat message is wrong"); } + + TEST_METHOD(ParseSendChatMessage) + { + std::string chatMessage = "Message"; + + SocketMessages::SendChatMessage sendChatMessage{}; + sendChatMessage.SetChatMessage(chatMessage); + + std::vector messageBuffer = sendChatMessage.Serialize(); + std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); + + Assert::IsFalse(sendChatMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); + Assert::IsTrue(sendChatMessage.ParseBody(buffer), L"Valid data can't be parsed"); + + Assert::AreEqual(chatMessage, sendChatMessage.GetChatMessage(), L"Chat message is wrong"); + } }; } diff --git a/ServerUnitTest/SerializeMessageUnitTests.cpp b/ServerUnitTest/SerializeMessageUnitTests.cpp index b57770e..200bb2e 100644 --- a/ServerUnitTest/SerializeMessageUnitTests.cpp +++ b/ServerUnitTest/SerializeMessageUnitTests.cpp @@ -154,14 +154,6 @@ namespace ServerUnitTest { SocketMessages::SendChatMessage message{}; - std::string validUsername = "Username"; - std::string invalidUsername1 = "0"; - std::string invalidUsername2 = "012345678901234567890123456789012"; - - Assert::IsTrue(message.SetAuthorUsername(validUsername), L"Valid username can't be set"); - Assert::IsFalse(message.SetAuthorUsername(invalidUsername1), L"Short username can be set"); - Assert::IsFalse(message.SetAuthorUsername(invalidUsername2), L"Long username can be set"); - std::string validMessage = "Message"; std::string invalidMessage1 = ""; std::string invalidMessage2(2001, 'a'); From 14092c186c3fc0e40a677780015bb9de8ff3046d Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Wed, 22 Mar 2023 21:13:15 +0100 Subject: [PATCH 010/203] Increase readability --- Server/ReceiveChatMessage.h | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/Server/ReceiveChatMessage.h b/Server/ReceiveChatMessage.h index bd19fb4..59b7700 100644 --- a/Server/ReceiveChatMessage.h +++ b/Server/ReceiveChatMessage.h @@ -21,25 +21,17 @@ namespace SocketMessages if (buffer.size() != GetBodySize()) return false; - std::memcpy( - std::bit_cast(&m_authorUsernameLength), - buffer.data(), - sizeof(m_authorUsernameLength) - ); - m_authorUsername = std::string( - buffer.begin() + sizeof(m_authorUsernameLength), - buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength - ); - - std::memcpy( - std::bit_cast(&m_chatMessageLength), - buffer.data() + sizeof(m_authorUsernameLength) + m_authorUsernameLength, - sizeof(m_chatMessageLength) - ); - m_chatMessage = std::string( - buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength), - buffer.end() - ); + std::memcpy(std::bit_cast(&m_authorUsernameLength), + buffer.data(), + sizeof(m_authorUsernameLength)); + m_authorUsername = std::string(buffer.begin() + sizeof(m_authorUsernameLength), + buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength); + + std::memcpy(std::bit_cast(&m_chatMessageLength), + buffer.data() + sizeof(m_authorUsernameLength) + m_authorUsernameLength, + sizeof(m_chatMessageLength)); + m_chatMessage = std::string(buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength), + buffer.end()); header.bodySize = GetBodySize(); @@ -66,7 +58,7 @@ namespace SocketMessages return buffer; } - uint64_t GetBodySize() const final + uint64_t GetBodySize() const final { return sizeof(m_authorUsernameLength) + m_authorUsernameLength From f2dec758cfb384603721f59bb7c209beae2a6959 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Fri, 24 Mar 2023 00:26:28 +0100 Subject: [PATCH 011/203] Improve thread safety --- Server/Connection.cpp | 7 +++-- Server/Connection.h | 5 ++++ Server/Coroutine.cpp | 1 + Server/Server.cpp | 49 ++++++++++++++++++++++++++++------- Server/Server.h | 7 ++++- Server/Server.vcxproj | 1 + Server/Server.vcxproj.filters | 3 +++ Shared/shared.cpp | 4 ++- 8 files changed, 64 insertions(+), 13 deletions(-) create mode 100644 Server/Coroutine.cpp diff --git a/Server/Connection.cpp b/Server/Connection.cpp index 8544f22..3f6f6a6 100644 --- a/Server/Connection.cpp +++ b/Server/Connection.cpp @@ -12,6 +12,7 @@ + Connection::Connection(SOCKET socket, Server& server) : m_server{ server } , m_socket{ socket } @@ -40,7 +41,11 @@ AsyncTask Connection::Listen() if (!(co_await EstablishConnection())) { + if (!m_disconnecting) + { + m_disconnecting = true; m_server.DisconnectClient(m_id); + } co_return; } @@ -159,7 +164,6 @@ AsyncOperation Connection::EstablishConnection() if (!(co_await CheckVersion())) { std::cout << " Client failed version check: " << std::dec << m_id << std::endl; - m_server.DisconnectClient(m_id); co_return false; } @@ -168,7 +172,6 @@ AsyncOperation Connection::EstablishConnection() if (!(co_await Identify())) { std::cout << " Client failed identify: " << std::dec << m_id << std::endl; - m_server.DisconnectClient(m_id); co_return false; } diff --git a/Server/Connection.h b/Server/Connection.h index 6584567..aebf9e5 100644 --- a/Server/Connection.h +++ b/Server/Connection.h @@ -28,6 +28,10 @@ class Connection : public std::enable_shared_from_this [[nodiscard]] constexpr Snowflake const& GetId() const noexcept { return m_id; } [[nodiscard]] constexpr std::jthread& GetThread() noexcept { return m_thread; } [[nodiscard]] constexpr std::shared_ptr const& GetUser() const noexcept { return m_user; } + [[nodiscard]] constexpr SOCKET const& GetSocket() const noexcept { return m_socket; } + + [[nodiscard]] constexpr bool const& IsDisconnecting() const noexcept { return m_disconnecting; } + constexpr void SetDisconnecting(bool const& disconnecting) noexcept { m_disconnecting = disconnecting; } private: AsyncOperation> ReceiveRawMessage(uint64_t const& bufferSize) const; @@ -43,4 +47,5 @@ class Connection : public std::enable_shared_from_this SOCKET m_socket; std::shared_ptr m_user{ std::make_shared() }; std::jthread m_thread; + bool m_disconnecting{ false }; }; diff --git a/Server/Coroutine.cpp b/Server/Coroutine.cpp new file mode 100644 index 0000000..51ccca4 --- /dev/null +++ b/Server/Coroutine.cpp @@ -0,0 +1 @@ +#include "Coroutine.h" \ No newline at end of file diff --git a/Server/Server.cpp b/Server/Server.cpp index 59538be..4472577 100644 --- a/Server/Server.cpp +++ b/Server/Server.cpp @@ -21,9 +21,21 @@ void Server::Run(std::string_view const& port) std::cout << "Pine - version " << std::hex << CurrentVersion << "\nServer Listening" << std::endl; m_stop = false; + bool firstLoop = true; + while (!m_stop) { + if (firstLoop) + { + firstLoop = false; + m_cv.notify_one(); + } + SOCKET clientSocket = accept(m_socket, nullptr, nullptr); + + if (m_stop) + break; + if (clientSocket == INVALID_SOCKET) { closesocket(m_socket); @@ -33,7 +45,7 @@ void Server::Run(std::string_view const& port) auto client = std::make_shared(clientSocket, *this); client->Listen(); - m_clients.push_back(std::move(client)); + m_clients.push_back(std::move(client)); // TODO: Make sure this is safe } std::cout << "Socket stops listening" << std::endl; @@ -44,35 +56,54 @@ void Server::Run(std::string_view const& port) void Server::Stop() { m_stop = true; + + for (auto const& client : m_clients) + { + if (!client->IsDisconnecting()) + DisconnectClient(client->GetId()); + } + + shutdown(m_socket, SD_BOTH); closesocket(m_socket); WSACleanup(); + m_cv.notify_one(); } AsyncTask Server::DisconnectClient(Snowflake clientId) { - if (m_thread.joinable()) - { - m_thread.request_stop(); - m_thread.join(); - } + std::unique_lock lock{ m_disconnectMutex }; co_await SwitchThread(m_thread); // Allow the connection thread to end - auto client = std::ranges::find_if(std::ranges::begin(m_clients), std::ranges::end(m_clients), [clientId](auto const& potentialClient) + auto client = std::ranges::find_if(m_clients, [clientId](auto const& potentialClient) { return potentialClient->GetId() == clientId; }); - if (client == m_clients.end()) - throw ServerException{ "Trying to disconnect inexistent client." }; + if (client != m_clients.end()) + { + (*client)->SetDisconnecting(true); + closesocket((*client)->GetSocket()); + shutdown((*client)->GetSocket(), SD_BOTH); if ((*client)->GetThread().joinable()) + { + (*client)->GetThread().request_stop(); (*client)->GetThread().join(); + } + m_clients.erase(client); } + m_cv.notify_all(); + lock.unlock(); ///////////////////////////////////// fails here + lock.release(); + + co_return; +} + AsyncTask Server::MessageClient(std::shared_ptr const& client, std::shared_ptr const& message) const diff --git a/Server/Server.h b/Server/Server.h index f9ba3fa..75ce0e7 100644 --- a/Server/Server.h +++ b/Server/Server.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "Connection.h" @@ -26,11 +27,15 @@ class Server AsyncTask OnMessage(std::shared_ptr const& client, std::shared_ptr const&); private: +#endif + std::condition_variable m_cv; + std::mutex m_disconnectMutex; + bool InitSocket(std::string_view const& port); std::deque> m_clients; - bool m_stop = false; + bool m_stop = true; SOCKET m_socket = INVALID_SOCKET; std::jthread m_thread; }; diff --git a/Server/Server.vcxproj b/Server/Server.vcxproj index 942fa37..fc7eac8 100644 --- a/Server/Server.vcxproj +++ b/Server/Server.vcxproj @@ -140,6 +140,7 @@ + diff --git a/Server/Server.vcxproj.filters b/Server/Server.vcxproj.filters index 6f6a23b..8b0d585 100644 --- a/Server/Server.vcxproj.filters +++ b/Server/Server.vcxproj.filters @@ -45,6 +45,9 @@ Source Files + + Source Files + diff --git a/Shared/shared.cpp b/Shared/shared.cpp index 9e53edb..4c3afff 100644 --- a/Shared/shared.cpp +++ b/Shared/shared.cpp @@ -3,12 +3,14 @@ #define WIN32_LEAN_AND_MEAN #include -#include #include +#include +#include #include #include #include #include +#include #include #include #include From 047dc5d6c4f6d19984047aa400f26a9f1597d7dd Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Fri, 24 Mar 2023 00:29:18 +0100 Subject: [PATCH 012/203] Implement Unit Tests for connection --- Server/Connection.h | 4 +- Server/Server.cpp | 11 +- Server/Server.h | 2 + ServerUnitTest/ConnectionUnitTest.cpp | 123 ++++++++++++++++++ ServerUnitTest/ServerUnitTest.vcxproj | 17 +-- ServerUnitTest/ServerUnitTest.vcxproj.filters | 3 + 6 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 ServerUnitTest/ConnectionUnitTest.cpp diff --git a/Server/Connection.h b/Server/Connection.h index aebf9e5..e791633 100644 --- a/Server/Connection.h +++ b/Server/Connection.h @@ -32,8 +32,10 @@ class Connection : public std::enable_shared_from_this [[nodiscard]] constexpr bool const& IsDisconnecting() const noexcept { return m_disconnecting; } constexpr void SetDisconnecting(bool const& disconnecting) noexcept { m_disconnecting = disconnecting; } - + +#ifndef MS_CPP_UNITTESTFRAMEWORK private: +#endif AsyncOperation> ReceiveRawMessage(uint64_t const& bufferSize) const; AsyncTask SendRawMessage(std::vector const& buffer) const; diff --git a/Server/Server.cpp b/Server/Server.cpp index 4472577..c3827ef 100644 --- a/Server/Server.cpp +++ b/Server/Server.cpp @@ -75,7 +75,6 @@ AsyncTask Server::DisconnectClient(Snowflake clientId) { std::unique_lock lock{ m_disconnectMutex }; - co_await SwitchThread(m_thread); // Allow the connection thread to end auto client = std::ranges::find_if(m_clients, [clientId](auto const& potentialClient) { @@ -88,17 +87,17 @@ AsyncTask Server::DisconnectClient(Snowflake clientId) closesocket((*client)->GetSocket()); shutdown((*client)->GetSocket(), SD_BOTH); - if ((*client)->GetThread().joinable()) + if ((*client)->GetThread().joinable()) { (*client)->GetThread().request_stop(); - (*client)->GetThread().join(); + (*client)->GetThread().join(); } - m_clients.erase(client); -} + m_clients.erase(client); + } m_cv.notify_all(); - lock.unlock(); ///////////////////////////////////// fails here + lock.unlock(); lock.release(); co_return; diff --git a/Server/Server.h b/Server/Server.h index 75ce0e7..c90c215 100644 --- a/Server/Server.h +++ b/Server/Server.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -26,6 +27,7 @@ class Server AsyncTask OnConnect(std::shared_ptr const& client) const; AsyncTask OnMessage(std::shared_ptr const& client, std::shared_ptr const&); +#ifndef MS_CPP_UNITTESTFRAMEWORK private: #endif std::condition_variable m_cv; diff --git a/ServerUnitTest/ConnectionUnitTest.cpp b/ServerUnitTest/ConnectionUnitTest.cpp new file mode 100644 index 0000000..1c548f1 --- /dev/null +++ b/ServerUnitTest/ConnectionUnitTest.cpp @@ -0,0 +1,123 @@ +#include "CppUnitTest.h" + +#include +#include +#include +#include +#include +#include +#include + + +#include "Connection.h" +#include "Coroutine.h" +#include "Server.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +using namespace std::chrono_literals; + +namespace ServerUnitTest +{ + TEST_CLASS(ConnectionUnitTest) + { + public: + void StartServer() + { + serverThread = std::jthread{ [this]() { server.Run("45321"); } }; + + std::unique_lock lock(mutex); + server.m_cv.wait(lock, [this] { return !server.m_stop; }); + + lock.unlock(); + lock.release(); + } + + std::shared_ptr CreateConnection() + { + addrinfo hints{}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + addrinfo* result = nullptr; + int iResult = getaddrinfo("localhost", "45321", &hints, &result); + Assert::AreEqual(iResult, 0, L"Can't get address info"); + + SOCKET sendingSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + Assert::AreNotEqual(sendingSocket, INVALID_SOCKET, L"Can't start test socket"); + + iResult = connect(sendingSocket, result->ai_addr, static_cast(result->ai_addrlen)); + Assert::AreEqual(iResult, 0, L"Can't connect to the server"); + + freeaddrinfo(result); + + return std::make_shared(sendingSocket, server); + } + + Server server; + std::jthread serverThread; + std::mutex mutex; + + TEST_METHOD(EstablishConnection) + { + StartServer(); + + std::shared_ptr clientConnection = CreateConnection(); + + std::condition_variable cv; + bool finished = false; + [&]() -> AsyncTask { + co_await SwitchThread(clientConnection->GetThread()); + + std::vector token = co_await clientConnection->ReceiveRawMessage(sizeof(uint64_t)); + Assert::AreEqual(token.size(), sizeof(uint64_t), L"Wrong token size"); + + *std::bit_cast(token.data()) ^= 0xF007CAFEC0C0CA7E; + + co_await clientConnection->SendRawMessage(token); + + std::shared_ptr message = co_await clientConnection->ReceiveMessage(); + Assert::AreEqual(static_cast(message->header.messageType), + static_cast(SocketMessages::MessageType::HelloMessage), + L"Wrong message type instead of Hello"); + + message = std::make_shared(); + co_await clientConnection->SendMessage(message); + + message = co_await clientConnection->ReceiveMessage(); + + Assert::AreEqual(static_cast(message->header.messageType), + static_cast(SocketMessages::MessageType::AcknowledgeMessage), + L"Wrong message type instead of ACK"); + + closesocket(clientConnection->GetSocket()); + shutdown(clientConnection->GetSocket(), SD_BOTH); + + finished = true; + cv.notify_one(); + + co_return; + }(); + + std::unique_lock lock{ mutex }; + cv.wait(lock, [&finished] { return finished; }); + + lock.unlock(); + lock.release(); + + if (clientConnection->GetThread().joinable()) + { + clientConnection->GetThread().request_stop(); + clientConnection->GetThread().join(); + } + + + server.Stop(); + + if (serverThread.joinable()) + serverThread.join(); + + return; + } + }; +} \ No newline at end of file diff --git a/ServerUnitTest/ServerUnitTest.vcxproj b/ServerUnitTest/ServerUnitTest.vcxproj index 7d66719..835a21c 100644 --- a/ServerUnitTest/ServerUnitTest.vcxproj +++ b/ServerUnitTest/ServerUnitTest.vcxproj @@ -96,13 +96,13 @@ _DEBUG;%(PreprocessorDefinitions) true pch.h - stdcpp20 + stdcpplatest Windows $(SolutionDir)Server\x64\Debug;$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) LinkVerbose - Message.obj;%(AdditionalDependencies) + Connection.obj;Coroutine.obj;Coroutine.h.obj;Message.obj;Server.obj;ServerException.h.obj;ServerOnMessage.obj;User.obj;%(AdditionalDependencies) @@ -114,13 +114,13 @@ WIN32;_DEBUG;%(PreprocessorDefinitions) true pch.h - stdcpp20 + stdcpplatest Windows $(SolutionDir)Server\x64\Debug;$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) LinkVerbose - Message.obj;%(AdditionalDependencies) + Connection.obj;Coroutine.obj;Coroutine.h.obj;Message.obj;Server.obj;ServerException.h.obj;ServerOnMessage.obj;User.obj;%(AdditionalDependencies) @@ -134,7 +134,7 @@ WIN32;NDEBUG;%(PreprocessorDefinitions) true pch.h - stdcpp20 + stdcpplatest Windows @@ -142,7 +142,7 @@ true $(SolutionDir)Server\x64\Debug;$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) LinkVerbose - Message.obj;%(AdditionalDependencies) + Connection.obj;Coroutine.obj;Coroutine.h.obj;Message.obj;Server.obj;ServerException.h.obj;ServerOnMessage.obj;User.obj;%(AdditionalDependencies) @@ -156,7 +156,7 @@ NDEBUG;%(PreprocessorDefinitions) true pch.h - stdcpp20 + stdcpplatest Windows @@ -164,10 +164,11 @@ true $(SolutionDir)Server\x64\Debug;$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) LinkVerbose - Message.obj;%(AdditionalDependencies) + Connection.obj;Coroutine.obj;Coroutine.h.obj;Message.obj;Server.obj;ServerException.h.obj;ServerOnMessage.obj;User.obj;%(AdditionalDependencies) + diff --git a/ServerUnitTest/ServerUnitTest.vcxproj.filters b/ServerUnitTest/ServerUnitTest.vcxproj.filters index 6a76d6d..14da80a 100644 --- a/ServerUnitTest/ServerUnitTest.vcxproj.filters +++ b/ServerUnitTest/ServerUnitTest.vcxproj.filters @@ -21,5 +21,8 @@ Source Files + + Source Files + \ No newline at end of file From 928183a2e995dbf7e4282847a81f667058851503 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Fri, 24 Mar 2023 00:29:41 +0100 Subject: [PATCH 013/203] Make connection support more message types --- Server/Connection.cpp | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/Server/Connection.cpp b/Server/Connection.cpp index 3f6f6a6..622fa61 100644 --- a/Server/Connection.cpp +++ b/Server/Connection.cpp @@ -44,7 +44,7 @@ AsyncTask Connection::Listen() if (!m_disconnecting) { m_disconnecting = true; - m_server.DisconnectClient(m_id); + m_server.DisconnectClient(m_id); } co_return; } @@ -113,22 +113,43 @@ AsyncOperation> Connection::ReceiveMess std::vector body = co_await ReceiveRawMessage(header.bodySize); - if (header.messageType == SocketMessages::MessageType::HelloMessage) + + switch (header.messageType) { + using namespace SocketMessages; + + case MessageType::HelloMessage: message = std::make_shared(); if (!std::dynamic_pointer_cast(message)->ParseBody(body)) co_return message; - } - else if (header.messageType == SocketMessages::MessageType::IdentifyMessage) - { + break; + + case MessageType::IdentifyMessage: message = std::make_shared(); if (!std::dynamic_pointer_cast(message)->ParseBody(body)) co_return message; - } - else + break; + + case MessageType::KeepAliveMessage: + message = std::make_shared(); + + if (!std::dynamic_pointer_cast(message)->ParseBody(body)) + co_return message; + break; + + case MessageType::SendChatMessage: + message = std::make_shared(); + + if (!std::dynamic_pointer_cast(message)->ParseBody(body)) + co_return message; + break; + + default: co_return message; + break; + } message->header = header; @@ -209,7 +230,7 @@ AsyncOperation Connection::Identify() co_return false; auto&& identifyMessage = std::dynamic_pointer_cast(message); - + m_user->m_username = identifyMessage->GetUsername(); m_user->m_isLoggedIn = true; From 494e1033a89f6c8fce74400a883bf0ee26bf88ce Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Fri, 24 Mar 2023 14:32:56 +0100 Subject: [PATCH 014/203] Add id to message header --- Server/Message.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Server/Message.cpp b/Server/Message.cpp index b02c76a..ecd3871 100644 --- a/Server/Message.cpp +++ b/Server/Message.cpp @@ -18,17 +18,44 @@ namespace SocketMessages switch (uint8_t messageType_ = buffer[0]) { - using enum SocketMessages::MessageType; + using enum MessageType; + + case static_cast(AcknowledgeMessage): + messageType = AcknowledgeMessage; + break; + case static_cast(HelloMessage): messageType = HelloMessage; break; + case static_cast(IdentifyMessage): messageType = IdentifyMessage; break; + + case static_cast(KeepAliveMessage): + messageType = KeepAliveMessage; + break; + + case static_cast(SendChatMessage): + messageType = SendChatMessage; + break; + + case static_cast(ReceiveChatMessage): + messageType = ReceiveChatMessage; + break; + + case static_cast(ErrorMessage): + messageType = ErrorMessage; + break; + default: messageType = InvalidMessage; break; } + + uint64_t id; + std::memcpy(&id, &buffer[sizeof(messageType) + sizeof(bodySize)], sizeof(id)); + messageId = Snowflake{ id }; } std::vector MessageHeader::Serialize() const From 98b8813b49c2e69d792c660e264b1c7961359a95 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Fri, 24 Mar 2023 14:33:58 +0100 Subject: [PATCH 015/203] Implement EstablishConnection #3 --- Server/Connection.cpp | 56 ++++++++++++++++++--------- Server/Connection.h | 14 ++++--- Server/IdentifyMessage.h | 3 +- ServerUnitTest/ConnectionUnitTest.cpp | 45 ++++++++++++++++++++- 4 files changed, 91 insertions(+), 27 deletions(-) diff --git a/Server/Connection.cpp b/Server/Connection.cpp index 622fa61..4f92182 100644 --- a/Server/Connection.cpp +++ b/Server/Connection.cpp @@ -99,7 +99,7 @@ AsyncTask Connection::SendRawMessage(std::vector const& buffer) const -AsyncOperation> Connection::ReceiveMessage() const +AsyncOperation> Connection::ReceiveMessage() { auto message = std::make_shared(); @@ -116,43 +116,60 @@ AsyncOperation> Connection::ReceiveMess switch (header.messageType) { - using namespace SocketMessages; + using enum SocketMessages::MessageType; - case MessageType::HelloMessage: + case AcknowledgeMessage: + message = std::make_shared(); + + if (!std::dynamic_pointer_cast(message)->ParseBody(body)) + co_return message; + break; + + case HelloMessage: message = std::make_shared(); if (!std::dynamic_pointer_cast(message)->ParseBody(body)) co_return message; break; - case MessageType::IdentifyMessage: + case IdentifyMessage: message = std::make_shared(); if (!std::dynamic_pointer_cast(message)->ParseBody(body)) co_return message; break; - case MessageType::KeepAliveMessage: + case KeepAliveMessage: message = std::make_shared(); if (!std::dynamic_pointer_cast(message)->ParseBody(body)) co_return message; break; - case MessageType::SendChatMessage: + case SendChatMessage: message = std::make_shared(); if (!std::dynamic_pointer_cast(message)->ParseBody(body)) co_return message; break; + case ReceiveChatMessage: + message = std::make_shared(); + + if (!std::dynamic_pointer_cast(message)->ParseBody(body)) + co_return message; + break; + default: co_return message; break; } + message->header = header; + m_lastMessageId = message->header.messageId; + co_return message; } @@ -160,12 +177,7 @@ AsyncOperation> Connection::ReceiveMess AsyncTask Connection::SendMessage(std::shared_ptr const& message) const { - std::vector buffer; - - if (message->header.messageType == SocketMessages::MessageType::HelloMessage) - buffer = std::dynamic_pointer_cast(message)->Serialize(); - else - throw ServerException{ "Trying to send unknown message type." }; + std::vector buffer = message->Serialize(); co_await SendRawMessage(buffer); } @@ -188,6 +200,8 @@ AsyncOperation Connection::EstablishConnection() co_return false; } + co_await SendAck(); + std::cout << " Client passed version check: " << std::dec << m_id << std::endl; if (!(co_await Identify())) @@ -196,6 +210,8 @@ AsyncOperation Connection::EstablishConnection() co_return false; } + co_await SendAck(); + std::cout << " Client successfully identified in as \"" << m_user->m_username << "\": " << std::dec << m_id << std::endl; co_return true; @@ -203,13 +219,11 @@ AsyncOperation Connection::EstablishConnection() -AsyncOperation Connection::CheckVersion() const +AsyncOperation Connection::CheckVersion() { - co_await SendMessage(std::static_pointer_cast( - std::make_shared() - )); + co_await SendMessage(std::make_shared()); - std::shared_ptr hello = co_await ReceiveMessage(); + auto&& hello = co_await ReceiveMessage(); if (hello->header.messageType != SocketMessages::MessageType::HelloMessage) co_return false; @@ -262,4 +276,10 @@ AsyncOperation Connection::ValidateConnection() const if (std::ranges::equal(keyBuffer, response)) co_return true; co_return false; -} \ No newline at end of file +} + +AsyncTask Connection::SendAck() const +{ + auto message = std::make_shared(m_lastMessageId); + co_await SendMessage(message); +} diff --git a/Server/Connection.h b/Server/Connection.h index e791633..c480121 100644 --- a/Server/Connection.h +++ b/Server/Connection.h @@ -22,9 +22,12 @@ class Connection : public std::enable_shared_from_this AsyncTask Listen(); - AsyncOperation> ReceiveMessage() const; + AsyncOperation> ReceiveMessage(); AsyncTask SendMessage(std::shared_ptr const& message) const; + AsyncOperation> ReceiveRawMessage(uint64_t const& bufferSize) const; + AsyncTask SendRawMessage(std::vector const& buffer) const; + [[nodiscard]] constexpr Snowflake const& GetId() const noexcept { return m_id; } [[nodiscard]] constexpr std::jthread& GetThread() noexcept { return m_thread; } [[nodiscard]] constexpr std::shared_ptr const& GetUser() const noexcept { return m_user; } @@ -33,18 +36,17 @@ class Connection : public std::enable_shared_from_this [[nodiscard]] constexpr bool const& IsDisconnecting() const noexcept { return m_disconnecting; } constexpr void SetDisconnecting(bool const& disconnecting) noexcept { m_disconnecting = disconnecting; } -#ifndef MS_CPP_UNITTESTFRAMEWORK private: -#endif - AsyncOperation> ReceiveRawMessage(uint64_t const& bufferSize) const; - AsyncTask SendRawMessage(std::vector const& buffer) const; AsyncOperation EstablishConnection(); - AsyncOperation CheckVersion() const; + AsyncOperation CheckVersion(); AsyncOperation Identify(); AsyncOperation ValidateConnection() const; + AsyncTask SendAck() const; + Snowflake m_id; + Snowflake m_lastMessageId; Server& m_server; SOCKET m_socket; std::shared_ptr m_user{ std::make_shared() }; diff --git a/Server/IdentifyMessage.h b/Server/IdentifyMessage.h index b9caf03..4167ae7 100644 --- a/Server/IdentifyMessage.h +++ b/Server/IdentifyMessage.h @@ -19,7 +19,8 @@ namespace SocketMessages bool ParseBody(std::vector const& buffer) override { - if (buffer.size() != GetBodySize()) + if (buffer.size() < sizeof(m_usernameLength) + UsernameMinLength + || buffer.size() > sizeof(m_usernameLength) + UsernameMaxLength) return false; std::memcpy(std::bit_cast(&m_usernameLength), std::bit_cast(buffer.data()), sizeof(m_usernameLength)); diff --git a/ServerUnitTest/ConnectionUnitTest.cpp b/ServerUnitTest/ConnectionUnitTest.cpp index 1c548f1..4cc048a 100644 --- a/ServerUnitTest/ConnectionUnitTest.cpp +++ b/ServerUnitTest/ConnectionUnitTest.cpp @@ -69,26 +69,67 @@ namespace ServerUnitTest [&]() -> AsyncTask { co_await SwitchThread(clientConnection->GetThread()); + + Logger::WriteMessage("Waiting for token"); std::vector token = co_await clientConnection->ReceiveRawMessage(sizeof(uint64_t)); Assert::AreEqual(token.size(), sizeof(uint64_t), L"Wrong token size"); + Logger::WriteMessage("Received token"); *std::bit_cast(token.data()) ^= 0xF007CAFEC0C0CA7E; co_await clientConnection->SendRawMessage(token); + Logger::WriteMessage("Sent token"); + + + + Logger::WriteMessage("Waiting for Hello"); std::shared_ptr message = co_await clientConnection->ReceiveMessage(); Assert::AreEqual(static_cast(message->header.messageType), static_cast(SocketMessages::MessageType::HelloMessage), - L"Wrong message type instead of Hello"); + L"Wrong message type received instead of Hello"); + Logger::WriteMessage("Received Hello message"); + + + message = std::make_shared(); - co_await clientConnection->SendMessage(message); + uint64_t expectedId = message->header.messageId; + co_await clientConnection->SendMessage(message); + Logger::WriteMessage("Sent Hello message"); + Logger::WriteMessage("Waiting for ACK"); message = co_await clientConnection->ReceiveMessage(); + Assert::AreEqual(static_cast(message->header.messageType), + static_cast(SocketMessages::MessageType::AcknowledgeMessage), + L"Wrong message type instead of ACK"); + Logger::WriteMessage("Received ACK"); + + uint64_t actualId = + std::dynamic_pointer_cast(message) + ->GetAcknowledgedMessageId(); + Assert::AreEqual(expectedId, actualId, L"Wrong message id received"); + + + + + message = std::make_shared(); + std::dynamic_pointer_cast(message)->SetUsername("Username"); + expectedId = message->header.messageId; + co_await clientConnection->SendMessage(message); + Logger::WriteMessage("Sent Identify message"); + message = co_await clientConnection->ReceiveMessage(); Assert::AreEqual(static_cast(message->header.messageType), static_cast(SocketMessages::MessageType::AcknowledgeMessage), L"Wrong message type instead of ACK"); + Logger::WriteMessage("Received ACK"); + + actualId = std::dynamic_pointer_cast(message) + ->GetAcknowledgedMessageId(); + Assert::AreEqual(expectedId, actualId, L"Wrong message id received"); + + closesocket(clientConnection->GetSocket()); shutdown(clientConnection->GetSocket(), SD_BOTH); From 32f0f2b97a3104ce54e56b3830269ea281b26d58 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Fri, 24 Mar 2023 14:37:54 +0100 Subject: [PATCH 016/203] Remove logging --- ServerUnitTest/ConnectionUnitTest.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ServerUnitTest/ConnectionUnitTest.cpp b/ServerUnitTest/ConnectionUnitTest.cpp index 4cc048a..641e411 100644 --- a/ServerUnitTest/ConnectionUnitTest.cpp +++ b/ServerUnitTest/ConnectionUnitTest.cpp @@ -70,10 +70,9 @@ namespace ServerUnitTest co_await SwitchThread(clientConnection->GetThread()); - Logger::WriteMessage("Waiting for token"); + std::vector token = co_await clientConnection->ReceiveRawMessage(sizeof(uint64_t)); Assert::AreEqual(token.size(), sizeof(uint64_t), L"Wrong token size"); - Logger::WriteMessage("Received token"); *std::bit_cast(token.data()) ^= 0xF007CAFEC0C0CA7E; @@ -83,12 +82,10 @@ namespace ServerUnitTest - Logger::WriteMessage("Waiting for Hello"); std::shared_ptr message = co_await clientConnection->ReceiveMessage(); Assert::AreEqual(static_cast(message->header.messageType), static_cast(SocketMessages::MessageType::HelloMessage), L"Wrong message type received instead of Hello"); - Logger::WriteMessage("Received Hello message"); @@ -97,13 +94,10 @@ namespace ServerUnitTest uint64_t expectedId = message->header.messageId; co_await clientConnection->SendMessage(message); - Logger::WriteMessage("Sent Hello message"); - Logger::WriteMessage("Waiting for ACK"); message = co_await clientConnection->ReceiveMessage(); Assert::AreEqual(static_cast(message->header.messageType), static_cast(SocketMessages::MessageType::AcknowledgeMessage), L"Wrong message type instead of ACK"); - Logger::WriteMessage("Received ACK"); uint64_t actualId = std::dynamic_pointer_cast(message) @@ -118,12 +112,10 @@ namespace ServerUnitTest expectedId = message->header.messageId; co_await clientConnection->SendMessage(message); - Logger::WriteMessage("Sent Identify message"); message = co_await clientConnection->ReceiveMessage(); Assert::AreEqual(static_cast(message->header.messageType), static_cast(SocketMessages::MessageType::AcknowledgeMessage), L"Wrong message type instead of ACK"); - Logger::WriteMessage("Received ACK"); actualId = std::dynamic_pointer_cast(message) ->GetAcknowledgedMessageId(); From f4b317ad7c930a82cf4e2e15dfd8ea236d75519b Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Fri, 24 Mar 2023 14:43:26 +0100 Subject: [PATCH 017/203] Increase thread safety --- Server/Server.cpp | 6 ++++-- Server/Server.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Server/Server.cpp b/Server/Server.cpp index c3827ef..7792df6 100644 --- a/Server/Server.cpp +++ b/Server/Server.cpp @@ -45,7 +45,9 @@ void Server::Run(std::string_view const& port) auto client = std::make_shared(clientSocket, *this); client->Listen(); - m_clients.push_back(std::move(client)); // TODO: Make sure this is safe + + std::unique_lock lock{ m_mutateClients }; + m_clients.push_back(std::move(client)); } std::cout << "Socket stops listening" << std::endl; @@ -73,7 +75,7 @@ void Server::Stop() AsyncTask Server::DisconnectClient(Snowflake clientId) { - std::unique_lock lock{ m_disconnectMutex }; + std::unique_lock lock{ m_mutateClients }; auto client = std::ranges::find_if(m_clients, [clientId](auto const& potentialClient) diff --git a/Server/Server.h b/Server/Server.h index c90c215..2397e2a 100644 --- a/Server/Server.h +++ b/Server/Server.h @@ -31,7 +31,7 @@ class Server private: #endif std::condition_variable m_cv; - std::mutex m_disconnectMutex; + std::mutex m_mutateClients; bool InitSocket(std::string_view const& port); From 4fe5ca5fc5155677f0e68168929d270000dafb9a Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Fri, 24 Mar 2023 15:00:03 +0100 Subject: [PATCH 018/203] Add unacceptable messages type to server message handler --- Server/ServerOnMessage.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Server/ServerOnMessage.cpp b/Server/ServerOnMessage.cpp index 1fa359a..bc7464a 100644 --- a/Server/ServerOnMessage.cpp +++ b/Server/ServerOnMessage.cpp @@ -11,5 +11,30 @@ AsyncTask Server::OnMessage(std::shared_ptr const& client, std::shar { std::cout << " Message received from client: " << std::dec << client->GetId() << std::endl; + switch (message->header.messageType) + { + using enum SocketMessages::MessageType; + + case AcknowledgeMessage: + [[fallthroug]] + case ErrorMessage: + [[fallthroug]] + case ReceiveChatMessage: + std::cout << " Message type not expected from client: " << std::dec << static_cast(message->header.messageType) << std::endl; + DisconnectClient(client->GetId()); + break; + + case HelloMessage: + [[fallthroug]] + case IdentifyMessage: + std::cout << " Message type not expected from client now: " << std::dec << static_cast(message->header.messageType) << std::endl; + break; + + default: + std::cout << " Unknown message type: " << std::dec << static_cast(message->header.messageType) << std::endl; + DisconnectClient(client->GetId()); + break; + } + co_return; } From c57cf71020cf5c67215f2c7bd820f1e8eb2439cc Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Fri, 24 Mar 2023 15:03:28 +0100 Subject: [PATCH 019/203] Remove useless variable --- Server/Connection.cpp | 1 - Server/User.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/Server/Connection.cpp b/Server/Connection.cpp index 4f92182..7869244 100644 --- a/Server/Connection.cpp +++ b/Server/Connection.cpp @@ -246,7 +246,6 @@ AsyncOperation Connection::Identify() auto&& identifyMessage = std::dynamic_pointer_cast(message); m_user->m_username = identifyMessage->GetUsername(); - m_user->m_isLoggedIn = true; co_return true; } diff --git a/Server/User.h b/Server/User.h index 733bee1..94e24cd 100644 --- a/Server/User.h +++ b/Server/User.h @@ -12,10 +12,8 @@ class User : public std::enable_shared_from_this ~User() = default; [[nodiscard]] constexpr std::string const& GetUsername() const noexcept { return m_username; } - [[nodiscard]] constexpr bool const& IsLoggedIn() const noexcept { return m_isLoggedIn; } private: - bool m_isLoggedIn{ false }; std::string m_username{ "" }; friend class Connection; From 4170d320a9c56c161018ff524ded8dac15386250 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Sat, 25 Mar 2023 00:01:41 +0100 Subject: [PATCH 020/203] Implement Send and Receive chat message #4 --- Server/Connection.cpp | 10 +- Server/Connection.h | 8 +- Server/ReceiveChatMessage.h | 13 ++- Server/SendChatMessage.h | 3 +- Server/Server.cpp | 4 +- Server/Server.h | 2 +- Server/ServerOnMessage.cpp | 24 ++++- ServerUnitTest/ConnectionUnitTest.cpp | 131 ++++++++++++++++++++++---- 8 files changed, 163 insertions(+), 32 deletions(-) diff --git a/Server/Connection.cpp b/Server/Connection.cpp index 7869244..0b55180 100644 --- a/Server/Connection.cpp +++ b/Server/Connection.cpp @@ -13,9 +13,10 @@ -Connection::Connection(SOCKET socket, Server& server) +Connection::Connection(SOCKET socket, Server& server, bool serverConnection) : m_server{ server } , m_socket{ socket } + , m_serverConnection{ serverConnection } { std::cout << "New client trying to connect: " << std::dec << m_id << std::endl; } @@ -77,6 +78,13 @@ AsyncOperation> Connection::ReceiveRawMessage(uint64_t cons int n = recv(m_socket, std::bit_cast(buffer.data()), static_cast(bufferSize), 0); if (n == SOCKET_ERROR) { + int error = WSAGetLastError(); + + if (error == WSAECONNRESET || error == WSAECONNABORTED && m_serverConnection) + { + co_return buffer; + } + throw ServerException{ "Failed to receive message: " + WSAGetLastError() }; } diff --git a/Server/Connection.h b/Server/Connection.h index c480121..71a71e8 100644 --- a/Server/Connection.h +++ b/Server/Connection.h @@ -17,7 +17,7 @@ class Server; class Connection : public std::enable_shared_from_this { public: - Connection(SOCKET socket, Server& server); + Connection(SOCKET socket, Server& server, bool serverConnection = true); ~Connection(); AsyncTask Listen(); @@ -36,6 +36,8 @@ class Connection : public std::enable_shared_from_this [[nodiscard]] constexpr bool const& IsDisconnecting() const noexcept { return m_disconnecting; } constexpr void SetDisconnecting(bool const& disconnecting) noexcept { m_disconnecting = disconnecting; } + AsyncTask SendAck() const; + private: AsyncOperation EstablishConnection(); @@ -43,13 +45,13 @@ class Connection : public std::enable_shared_from_this AsyncOperation Identify(); AsyncOperation ValidateConnection() const; - AsyncTask SendAck() const; - Snowflake m_id; Snowflake m_lastMessageId; Server& m_server; SOCKET m_socket; std::shared_ptr m_user{ std::make_shared() }; std::jthread m_thread; + bool m_disconnecting{ false }; + bool m_serverConnection{ true }; }; diff --git a/Server/ReceiveChatMessage.h b/Server/ReceiveChatMessage.h index 59b7700..2829e0f 100644 --- a/Server/ReceiveChatMessage.h +++ b/Server/ReceiveChatMessage.h @@ -18,18 +18,29 @@ namespace SocketMessages bool ParseBody(std::vector const& buffer) override { - if (buffer.size() != GetBodySize()) + if (buffer.size() < sizeof(m_authorUsernameLength) + UsernameMinLength + sizeof(m_chatMessageLength) + ChatMessageMinLength) + return false; + + if (buffer.size() > sizeof(m_authorUsernameLength) + UsernameMaxLength + sizeof(m_chatMessageLength) + ChatMessageMaxLength) return false; std::memcpy(std::bit_cast(&m_authorUsernameLength), buffer.data(), sizeof(m_authorUsernameLength)); + + if (buffer.size() < sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength) + ChatMessageMinLength) + return false; + m_authorUsername = std::string(buffer.begin() + sizeof(m_authorUsernameLength), buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength); std::memcpy(std::bit_cast(&m_chatMessageLength), buffer.data() + sizeof(m_authorUsernameLength) + m_authorUsernameLength, sizeof(m_chatMessageLength)); + + if (buffer.size() != sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength) + m_chatMessageLength) + return false; + m_chatMessage = std::string(buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength), buffer.end()); diff --git a/Server/SendChatMessage.h b/Server/SendChatMessage.h index 890ceb3..21f399f 100644 --- a/Server/SendChatMessage.h +++ b/Server/SendChatMessage.h @@ -20,7 +20,8 @@ namespace SocketMessages bool ParseBody(std::vector const& buffer) override { - if (buffer.size() != GetBodySize()) + if (buffer.size() < sizeof(m_chatMessageLength) + ChatMessageMinLength + || buffer.size() > sizeof (m_chatMessageLength) + ChatMessageMaxLength) return false; std::memcpy(std::bit_cast(&m_chatMessageLength), diff --git a/Server/Server.cpp b/Server/Server.cpp index 7792df6..06f450e 100644 --- a/Server/Server.cpp +++ b/Server/Server.cpp @@ -83,7 +83,7 @@ AsyncTask Server::DisconnectClient(Snowflake clientId) return potentialClient->GetId() == clientId; }); - if (client != m_clients.end()) + if (client != m_clients.end() && !(*client)->IsDisconnecting()) { (*client)->SetDisconnecting(true); closesocket((*client)->GetSocket()); @@ -99,8 +99,6 @@ AsyncTask Server::DisconnectClient(Snowflake clientId) } m_cv.notify_all(); - lock.unlock(); - lock.release(); co_return; } diff --git a/Server/Server.h b/Server/Server.h index 2397e2a..386f5fe 100644 --- a/Server/Server.h +++ b/Server/Server.h @@ -25,7 +25,7 @@ class Server AsyncTask DisconnectClient(Snowflake clientId); AsyncTask MessageClient(std::shared_ptr const& client, std::shared_ptr const& message) const; AsyncTask OnConnect(std::shared_ptr const& client) const; - AsyncTask OnMessage(std::shared_ptr const& client, std::shared_ptr const&); + AsyncTask OnMessage(std::shared_ptr client, std::shared_ptr message); #ifndef MS_CPP_UNITTESTFRAMEWORK private: diff --git a/Server/ServerOnMessage.cpp b/Server/ServerOnMessage.cpp index bc7464a..385ec75 100644 --- a/Server/ServerOnMessage.cpp +++ b/Server/ServerOnMessage.cpp @@ -7,7 +7,7 @@ #include #include -AsyncTask Server::OnMessage(std::shared_ptr const& client, std::shared_ptr const& message) +AsyncTask Server::OnMessage(std::shared_ptr client, std::shared_ptr message) { std::cout << " Message received from client: " << std::dec << client->GetId() << std::endl; @@ -16,20 +16,36 @@ AsyncTask Server::OnMessage(std::shared_ptr const& client, std::shar using enum SocketMessages::MessageType; case AcknowledgeMessage: - [[fallthroug]] case ErrorMessage: - [[fallthroug]] case ReceiveChatMessage: std::cout << " Message type not expected from client: " << std::dec << static_cast(message->header.messageType) << std::endl; DisconnectClient(client->GetId()); break; case HelloMessage: - [[fallthroug]] case IdentifyMessage: std::cout << " Message type not expected from client now: " << std::dec << static_cast(message->header.messageType) << std::endl; break; + case SendChatMessage: + { + std::cout << " Received chat message from client: " << std::dec << client->GetId() << std::endl; + auto receiveChat = std::make_shared(); + receiveChat->SetAuthorUsername(client->GetUser()->GetUsername()); + receiveChat->SetChatMessage(std::dynamic_pointer_cast(message)->GetChatMessage()); + + co_await client->SendAck(); + + for (auto const& iteratedClient : m_clients) + { + if (iteratedClient->GetId() == client->GetId()) + continue; + co_await client->SendMessage(receiveChat); + } + + break; + } + default: std::cout << " Unknown message type: " << std::dec << static_cast(message->header.messageType) << std::endl; DisconnectClient(client->GetId()); diff --git a/ServerUnitTest/ConnectionUnitTest.cpp b/ServerUnitTest/ConnectionUnitTest.cpp index 641e411..90af926 100644 --- a/ServerUnitTest/ConnectionUnitTest.cpp +++ b/ServerUnitTest/ConnectionUnitTest.cpp @@ -51,7 +51,31 @@ namespace ServerUnitTest freeaddrinfo(result); - return std::make_shared(sendingSocket, server); + return std::make_shared(sendingSocket, server, false); + } + + AsyncTask ConnectClient(std::shared_ptr client, std::string username) const + { + std::vector token = co_await client->ReceiveRawMessage(sizeof(uint64_t)); + *std::bit_cast(token.data()) ^= 0xF007CAFEC0C0CA7E; + co_await client->SendRawMessage(token); + + // Hello + std::shared_ptr message = co_await client->ReceiveMessage(); + + message = std::make_shared(); + // Hello + co_await client->SendMessage(message); + // ACK + message = co_await client->ReceiveMessage(); + + message = std::make_shared(); + std::dynamic_pointer_cast(message)->SetUsername(username); + + // Identify + co_await client->SendMessage(message); + // ACK + message = co_await client->ReceiveMessage(); } Server server; @@ -62,11 +86,12 @@ namespace ServerUnitTest { StartServer(); - std::shared_ptr clientConnection = CreateConnection(); - std::condition_variable cv; bool finished = false; + [&]() -> AsyncTask { + std::shared_ptr clientConnection = CreateConnection(); + co_await SwitchThread(clientConnection->GetThread()); @@ -77,7 +102,6 @@ namespace ServerUnitTest *std::bit_cast(token.data()) ^= 0xF007CAFEC0C0CA7E; co_await clientConnection->SendRawMessage(token); - Logger::WriteMessage("Sent token"); @@ -118,7 +142,7 @@ namespace ServerUnitTest L"Wrong message type instead of ACK"); actualId = std::dynamic_pointer_cast(message) - ->GetAcknowledgedMessageId(); + ->GetAcknowledgedMessageId(); Assert::AreEqual(expectedId, actualId, L"Wrong message id received"); @@ -132,25 +156,96 @@ namespace ServerUnitTest co_return; }(); - std::unique_lock lock{ mutex }; - cv.wait(lock, [&finished] { return finished; }); - - lock.unlock(); - lock.release(); - - if (clientConnection->GetThread().joinable()) { - clientConnection->GetThread().request_stop(); - clientConnection->GetThread().join(); + std::unique_lock lock{ mutex }; + cv.wait(lock, [&finished] { return finished; }); } - server.Stop(); + } + + TEST_METHOD(SendChatMessage) + { + StartServer(); + + std::condition_variable cv; + std::pair finished = { false, false }; + std::pair connected = { false, false }; + + std::string chatContent = "Hello world"; + std::string senderUsername = "Sender"; + + std::mutex readyMutex; + + + + [&]() -> AsyncTask { + auto receiverConnection = CreateConnection(); + + co_await SwitchThread(receiverConnection->GetThread()); + + co_await ConnectClient(receiverConnection, "Receiver"); - if (serverThread.joinable()) - serverThread.join(); + connected.second = true; + cv.notify_all(); - return; + auto&& message = co_await receiverConnection->ReceiveMessage(); + Assert::AreEqual(static_cast(message->header.messageType), + static_cast(SocketMessages::MessageType::SendChatMessage), + L"Wrong message type received instead of SendChatMessage"); + Assert::AreEqual(std::dynamic_pointer_cast(message)->GetChatMessage(), + chatContent, + L"Wrong message received"); + Assert::AreEqual(std::dynamic_pointer_cast(message)->GetAuthorUsername(), + senderUsername, + L"Wrong message received"); + + + closesocket(receiverConnection->GetSocket()); + shutdown(receiverConnection->GetSocket(), SD_BOTH); + + finished.second = true; + cv.notify_all(); + }(); + + + + [&]() -> AsyncTask { + auto senderConnection = CreateConnection(); + + co_await SwitchThread(senderConnection->GetThread()); + + co_await ConnectClient(senderConnection, senderUsername); + + { + std::unique_lock lock{ readyMutex }; + cv.wait(lock, [&connected] { return connected.second; }); + } + + auto message = std::make_shared(); + message->SetChatMessage(chatContent); + co_await senderConnection->SendMessage(message); + + auto&& Ack = co_await senderConnection->ReceiveMessage(); + Assert::AreEqual(static_cast(Ack->header.messageType), + static_cast(SocketMessages::MessageType::AcknowledgeMessage), + L"Wrong message type received instead of ACK"); + + closesocket(senderConnection->GetSocket()); + shutdown(senderConnection->GetSocket(), SD_BOTH); + + finished.first = true; + cv.notify_all(); + }(); + + + + { + std::unique_lock lock{ mutex }; + cv.wait(lock, [&finished] { return finished.first && finished.second; }); + } + + server.Stop(); } }; } \ No newline at end of file From 24b94c6272cc8a9b5a51ca283acb719a37fe9c0a Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Sat, 25 Mar 2023 00:01:41 +0100 Subject: [PATCH 021/203] Implement Send and Receive chat message #3 --- Server/Connection.cpp | 10 +- Server/Connection.h | 8 +- Server/ReceiveChatMessage.h | 13 ++- Server/SendChatMessage.h | 3 +- Server/Server.cpp | 4 +- Server/Server.h | 2 +- Server/ServerOnMessage.cpp | 24 ++++- ServerUnitTest/ConnectionUnitTest.cpp | 131 ++++++++++++++++++++++---- 8 files changed, 163 insertions(+), 32 deletions(-) diff --git a/Server/Connection.cpp b/Server/Connection.cpp index 7869244..0b55180 100644 --- a/Server/Connection.cpp +++ b/Server/Connection.cpp @@ -13,9 +13,10 @@ -Connection::Connection(SOCKET socket, Server& server) +Connection::Connection(SOCKET socket, Server& server, bool serverConnection) : m_server{ server } , m_socket{ socket } + , m_serverConnection{ serverConnection } { std::cout << "New client trying to connect: " << std::dec << m_id << std::endl; } @@ -77,6 +78,13 @@ AsyncOperation> Connection::ReceiveRawMessage(uint64_t cons int n = recv(m_socket, std::bit_cast(buffer.data()), static_cast(bufferSize), 0); if (n == SOCKET_ERROR) { + int error = WSAGetLastError(); + + if (error == WSAECONNRESET || error == WSAECONNABORTED && m_serverConnection) + { + co_return buffer; + } + throw ServerException{ "Failed to receive message: " + WSAGetLastError() }; } diff --git a/Server/Connection.h b/Server/Connection.h index c480121..71a71e8 100644 --- a/Server/Connection.h +++ b/Server/Connection.h @@ -17,7 +17,7 @@ class Server; class Connection : public std::enable_shared_from_this { public: - Connection(SOCKET socket, Server& server); + Connection(SOCKET socket, Server& server, bool serverConnection = true); ~Connection(); AsyncTask Listen(); @@ -36,6 +36,8 @@ class Connection : public std::enable_shared_from_this [[nodiscard]] constexpr bool const& IsDisconnecting() const noexcept { return m_disconnecting; } constexpr void SetDisconnecting(bool const& disconnecting) noexcept { m_disconnecting = disconnecting; } + AsyncTask SendAck() const; + private: AsyncOperation EstablishConnection(); @@ -43,13 +45,13 @@ class Connection : public std::enable_shared_from_this AsyncOperation Identify(); AsyncOperation ValidateConnection() const; - AsyncTask SendAck() const; - Snowflake m_id; Snowflake m_lastMessageId; Server& m_server; SOCKET m_socket; std::shared_ptr m_user{ std::make_shared() }; std::jthread m_thread; + bool m_disconnecting{ false }; + bool m_serverConnection{ true }; }; diff --git a/Server/ReceiveChatMessage.h b/Server/ReceiveChatMessage.h index 59b7700..2829e0f 100644 --- a/Server/ReceiveChatMessage.h +++ b/Server/ReceiveChatMessage.h @@ -18,18 +18,29 @@ namespace SocketMessages bool ParseBody(std::vector const& buffer) override { - if (buffer.size() != GetBodySize()) + if (buffer.size() < sizeof(m_authorUsernameLength) + UsernameMinLength + sizeof(m_chatMessageLength) + ChatMessageMinLength) + return false; + + if (buffer.size() > sizeof(m_authorUsernameLength) + UsernameMaxLength + sizeof(m_chatMessageLength) + ChatMessageMaxLength) return false; std::memcpy(std::bit_cast(&m_authorUsernameLength), buffer.data(), sizeof(m_authorUsernameLength)); + + if (buffer.size() < sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength) + ChatMessageMinLength) + return false; + m_authorUsername = std::string(buffer.begin() + sizeof(m_authorUsernameLength), buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength); std::memcpy(std::bit_cast(&m_chatMessageLength), buffer.data() + sizeof(m_authorUsernameLength) + m_authorUsernameLength, sizeof(m_chatMessageLength)); + + if (buffer.size() != sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength) + m_chatMessageLength) + return false; + m_chatMessage = std::string(buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength), buffer.end()); diff --git a/Server/SendChatMessage.h b/Server/SendChatMessage.h index 890ceb3..21f399f 100644 --- a/Server/SendChatMessage.h +++ b/Server/SendChatMessage.h @@ -20,7 +20,8 @@ namespace SocketMessages bool ParseBody(std::vector const& buffer) override { - if (buffer.size() != GetBodySize()) + if (buffer.size() < sizeof(m_chatMessageLength) + ChatMessageMinLength + || buffer.size() > sizeof (m_chatMessageLength) + ChatMessageMaxLength) return false; std::memcpy(std::bit_cast(&m_chatMessageLength), diff --git a/Server/Server.cpp b/Server/Server.cpp index 7792df6..06f450e 100644 --- a/Server/Server.cpp +++ b/Server/Server.cpp @@ -83,7 +83,7 @@ AsyncTask Server::DisconnectClient(Snowflake clientId) return potentialClient->GetId() == clientId; }); - if (client != m_clients.end()) + if (client != m_clients.end() && !(*client)->IsDisconnecting()) { (*client)->SetDisconnecting(true); closesocket((*client)->GetSocket()); @@ -99,8 +99,6 @@ AsyncTask Server::DisconnectClient(Snowflake clientId) } m_cv.notify_all(); - lock.unlock(); - lock.release(); co_return; } diff --git a/Server/Server.h b/Server/Server.h index 2397e2a..386f5fe 100644 --- a/Server/Server.h +++ b/Server/Server.h @@ -25,7 +25,7 @@ class Server AsyncTask DisconnectClient(Snowflake clientId); AsyncTask MessageClient(std::shared_ptr const& client, std::shared_ptr const& message) const; AsyncTask OnConnect(std::shared_ptr const& client) const; - AsyncTask OnMessage(std::shared_ptr const& client, std::shared_ptr const&); + AsyncTask OnMessage(std::shared_ptr client, std::shared_ptr message); #ifndef MS_CPP_UNITTESTFRAMEWORK private: diff --git a/Server/ServerOnMessage.cpp b/Server/ServerOnMessage.cpp index bc7464a..385ec75 100644 --- a/Server/ServerOnMessage.cpp +++ b/Server/ServerOnMessage.cpp @@ -7,7 +7,7 @@ #include #include -AsyncTask Server::OnMessage(std::shared_ptr const& client, std::shared_ptr const& message) +AsyncTask Server::OnMessage(std::shared_ptr client, std::shared_ptr message) { std::cout << " Message received from client: " << std::dec << client->GetId() << std::endl; @@ -16,20 +16,36 @@ AsyncTask Server::OnMessage(std::shared_ptr const& client, std::shar using enum SocketMessages::MessageType; case AcknowledgeMessage: - [[fallthroug]] case ErrorMessage: - [[fallthroug]] case ReceiveChatMessage: std::cout << " Message type not expected from client: " << std::dec << static_cast(message->header.messageType) << std::endl; DisconnectClient(client->GetId()); break; case HelloMessage: - [[fallthroug]] case IdentifyMessage: std::cout << " Message type not expected from client now: " << std::dec << static_cast(message->header.messageType) << std::endl; break; + case SendChatMessage: + { + std::cout << " Received chat message from client: " << std::dec << client->GetId() << std::endl; + auto receiveChat = std::make_shared(); + receiveChat->SetAuthorUsername(client->GetUser()->GetUsername()); + receiveChat->SetChatMessage(std::dynamic_pointer_cast(message)->GetChatMessage()); + + co_await client->SendAck(); + + for (auto const& iteratedClient : m_clients) + { + if (iteratedClient->GetId() == client->GetId()) + continue; + co_await client->SendMessage(receiveChat); + } + + break; + } + default: std::cout << " Unknown message type: " << std::dec << static_cast(message->header.messageType) << std::endl; DisconnectClient(client->GetId()); diff --git a/ServerUnitTest/ConnectionUnitTest.cpp b/ServerUnitTest/ConnectionUnitTest.cpp index 641e411..90af926 100644 --- a/ServerUnitTest/ConnectionUnitTest.cpp +++ b/ServerUnitTest/ConnectionUnitTest.cpp @@ -51,7 +51,31 @@ namespace ServerUnitTest freeaddrinfo(result); - return std::make_shared(sendingSocket, server); + return std::make_shared(sendingSocket, server, false); + } + + AsyncTask ConnectClient(std::shared_ptr client, std::string username) const + { + std::vector token = co_await client->ReceiveRawMessage(sizeof(uint64_t)); + *std::bit_cast(token.data()) ^= 0xF007CAFEC0C0CA7E; + co_await client->SendRawMessage(token); + + // Hello + std::shared_ptr message = co_await client->ReceiveMessage(); + + message = std::make_shared(); + // Hello + co_await client->SendMessage(message); + // ACK + message = co_await client->ReceiveMessage(); + + message = std::make_shared(); + std::dynamic_pointer_cast(message)->SetUsername(username); + + // Identify + co_await client->SendMessage(message); + // ACK + message = co_await client->ReceiveMessage(); } Server server; @@ -62,11 +86,12 @@ namespace ServerUnitTest { StartServer(); - std::shared_ptr clientConnection = CreateConnection(); - std::condition_variable cv; bool finished = false; + [&]() -> AsyncTask { + std::shared_ptr clientConnection = CreateConnection(); + co_await SwitchThread(clientConnection->GetThread()); @@ -77,7 +102,6 @@ namespace ServerUnitTest *std::bit_cast(token.data()) ^= 0xF007CAFEC0C0CA7E; co_await clientConnection->SendRawMessage(token); - Logger::WriteMessage("Sent token"); @@ -118,7 +142,7 @@ namespace ServerUnitTest L"Wrong message type instead of ACK"); actualId = std::dynamic_pointer_cast(message) - ->GetAcknowledgedMessageId(); + ->GetAcknowledgedMessageId(); Assert::AreEqual(expectedId, actualId, L"Wrong message id received"); @@ -132,25 +156,96 @@ namespace ServerUnitTest co_return; }(); - std::unique_lock lock{ mutex }; - cv.wait(lock, [&finished] { return finished; }); - - lock.unlock(); - lock.release(); - - if (clientConnection->GetThread().joinable()) { - clientConnection->GetThread().request_stop(); - clientConnection->GetThread().join(); + std::unique_lock lock{ mutex }; + cv.wait(lock, [&finished] { return finished; }); } - server.Stop(); + } + + TEST_METHOD(SendChatMessage) + { + StartServer(); + + std::condition_variable cv; + std::pair finished = { false, false }; + std::pair connected = { false, false }; + + std::string chatContent = "Hello world"; + std::string senderUsername = "Sender"; + + std::mutex readyMutex; + + + + [&]() -> AsyncTask { + auto receiverConnection = CreateConnection(); + + co_await SwitchThread(receiverConnection->GetThread()); + + co_await ConnectClient(receiverConnection, "Receiver"); - if (serverThread.joinable()) - serverThread.join(); + connected.second = true; + cv.notify_all(); - return; + auto&& message = co_await receiverConnection->ReceiveMessage(); + Assert::AreEqual(static_cast(message->header.messageType), + static_cast(SocketMessages::MessageType::SendChatMessage), + L"Wrong message type received instead of SendChatMessage"); + Assert::AreEqual(std::dynamic_pointer_cast(message)->GetChatMessage(), + chatContent, + L"Wrong message received"); + Assert::AreEqual(std::dynamic_pointer_cast(message)->GetAuthorUsername(), + senderUsername, + L"Wrong message received"); + + + closesocket(receiverConnection->GetSocket()); + shutdown(receiverConnection->GetSocket(), SD_BOTH); + + finished.second = true; + cv.notify_all(); + }(); + + + + [&]() -> AsyncTask { + auto senderConnection = CreateConnection(); + + co_await SwitchThread(senderConnection->GetThread()); + + co_await ConnectClient(senderConnection, senderUsername); + + { + std::unique_lock lock{ readyMutex }; + cv.wait(lock, [&connected] { return connected.second; }); + } + + auto message = std::make_shared(); + message->SetChatMessage(chatContent); + co_await senderConnection->SendMessage(message); + + auto&& Ack = co_await senderConnection->ReceiveMessage(); + Assert::AreEqual(static_cast(Ack->header.messageType), + static_cast(SocketMessages::MessageType::AcknowledgeMessage), + L"Wrong message type received instead of ACK"); + + closesocket(senderConnection->GetSocket()); + shutdown(senderConnection->GetSocket(), SD_BOTH); + + finished.first = true; + cv.notify_all(); + }(); + + + + { + std::unique_lock lock{ mutex }; + cv.wait(lock, [&finished] { return finished.first && finished.second; }); + } + + server.Stop(); } }; } \ No newline at end of file From ce3b07fb445edc1a3e47ca05d5fa36171fb6572d Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Sat, 25 Mar 2023 12:15:08 +0100 Subject: [PATCH 022/203] Move server from executable to library --- Pine.sln | 2 + PineServer/PineServer.vcxproj | 151 ++++++++++++++++++++++++++ PineServer/PineServer.vcxproj.filters | 22 ++++ {Server => PineServer}/main.cpp | 0 Server/Server.vcxproj | 9 +- Server/Server.vcxproj.filters | 3 - 6 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 PineServer/PineServer.vcxproj create mode 100644 PineServer/PineServer.vcxproj.filters rename {Server => PineServer}/main.cpp (100%) diff --git a/Pine.sln b/Pine.sln index 18cc104..206e72c 100644 --- a/Pine.sln +++ b/Pine.sln @@ -21,6 +21,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shared", "Shared\Shared.vcx EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ServerUnitTest", "ServerUnitTest\ServerUnitTest.vcxproj", "{B53D4CAB-38CB-4597-AF97-D37FCCDD5D14}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PineServer", "PineServer\PineServer.vcxproj", "{D3C781E2-F6E0-4334-BD33-5A514E22C366}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM diff --git a/PineServer/PineServer.vcxproj b/PineServer/PineServer.vcxproj new file mode 100644 index 0000000..108e181 --- /dev/null +++ b/PineServer/PineServer.vcxproj @@ -0,0 +1,151 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {d3c781e2-f6e0-4334-bd33-5a514e22c366} + PineServer + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)Server;%(AdditionalIncludeDirectories) + stdcpplatest + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)Server;%(AdditionalIncludeDirectories) + stdcpplatest + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)Server;%(AdditionalIncludeDirectories) + stdcpplatest + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)Server;%(AdditionalIncludeDirectories) + stdcpplatest + + + Console + true + true + true + + + + + {f11a6291-405c-4a94-ab56-f0027fdd4b4d} + + + {ffa917e0-c449-48c7-bdf3-9877cbd96b46} + + + + + + + + + \ No newline at end of file diff --git a/PineServer/PineServer.vcxproj.filters b/PineServer/PineServer.vcxproj.filters new file mode 100644 index 0000000..ce0c35c --- /dev/null +++ b/PineServer/PineServer.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/Server/main.cpp b/PineServer/main.cpp similarity index 100% rename from Server/main.cpp rename to PineServer/main.cpp diff --git a/Server/Server.vcxproj b/Server/Server.vcxproj index fc7eac8..1fb7db8 100644 --- a/Server/Server.vcxproj +++ b/Server/Server.vcxproj @@ -27,26 +27,26 @@ - Application + StaticLibrary true v143 Unicode - Application + StaticLibrary false v143 true Unicode - Application + StaticLibrary true v143 Unicode - Application + StaticLibrary false v143 true @@ -144,7 +144,6 @@ - diff --git a/Server/Server.vcxproj.filters b/Server/Server.vcxproj.filters index 8b0d585..ddc45cf 100644 --- a/Server/Server.vcxproj.filters +++ b/Server/Server.vcxproj.filters @@ -24,9 +24,6 @@ Source Files - - Source Files - Source Files From eb5e7688631a96e5810a91bfb4d9083095693f30 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Sat, 25 Mar 2023 12:18:54 +0100 Subject: [PATCH 023/203] Create server integration tests project --- Pine.sln | 66 +++++ .../EstablishConnectionIntegrationTests.cpp | 15 ++ .../ServerIntegrationTests.vcxproj | 172 ++++++++++++ .../ServerIntegrationTests.vcxproj.filters | 22 ++ ServerUnitTest/ConnectionUnitTest.cpp | 251 ------------------ ServerUnitTest/ServerUnitTest.vcxproj | 1 - 6 files changed, 275 insertions(+), 252 deletions(-) create mode 100644 ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp create mode 100644 ServerIntegrationTests/ServerIntegrationTests.vcxproj create mode 100644 ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters delete mode 100644 ServerUnitTest/ConnectionUnitTest.cpp diff --git a/Pine.sln b/Pine.sln index 206e72c..a175579 100644 --- a/Pine.sln +++ b/Pine.sln @@ -21,6 +21,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Shared", "Shared\Shared.vcx EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ServerUnitTest", "ServerUnitTest\ServerUnitTest.vcxproj", "{B53D4CAB-38CB-4597-AF97-D37FCCDD5D14}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ServerIntegrationTests", "ServerIntegrationTests\ServerIntegrationTests.vcxproj", "{2FF51663-B837-42E3-9E00-31B1AB857920}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PineServer", "PineServer\PineServer.vcxproj", "{D3C781E2-F6E0-4334-BD33-5A514E22C366}" EndProject Global @@ -187,6 +189,70 @@ Global {B53D4CAB-38CB-4597-AF97-D37FCCDD5D14}.RelWithDebInfo|x64.Build.0 = Release|x64 {B53D4CAB-38CB-4597-AF97-D37FCCDD5D14}.RelWithDebInfo|x86.ActiveCfg = Release|Win32 {B53D4CAB-38CB-4597-AF97-D37FCCDD5D14}.RelWithDebInfo|x86.Build.0 = Release|Win32 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Debug|ARM.ActiveCfg = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Debug|ARM.Build.0 = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Debug|arm64.ActiveCfg = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Debug|arm64.Build.0 = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Debug|x64.ActiveCfg = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Debug|x64.Build.0 = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Debug|x86.ActiveCfg = Debug|Win32 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Debug|x86.Build.0 = Debug|Win32 + {2FF51663-B837-42E3-9E00-31B1AB857920}.MinSizeRel|ARM.ActiveCfg = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.MinSizeRel|ARM.Build.0 = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.MinSizeRel|arm64.ActiveCfg = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.MinSizeRel|arm64.Build.0 = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.MinSizeRel|x64.ActiveCfg = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.MinSizeRel|x64.Build.0 = Debug|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.MinSizeRel|x86.ActiveCfg = Debug|Win32 + {2FF51663-B837-42E3-9E00-31B1AB857920}.MinSizeRel|x86.Build.0 = Debug|Win32 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Release|ARM.ActiveCfg = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Release|ARM.Build.0 = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Release|arm64.ActiveCfg = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Release|arm64.Build.0 = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Release|x64.ActiveCfg = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Release|x64.Build.0 = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Release|x86.ActiveCfg = Release|Win32 + {2FF51663-B837-42E3-9E00-31B1AB857920}.Release|x86.Build.0 = Release|Win32 + {2FF51663-B837-42E3-9E00-31B1AB857920}.RelWithDebInfo|ARM.ActiveCfg = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.RelWithDebInfo|ARM.Build.0 = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.RelWithDebInfo|arm64.ActiveCfg = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.RelWithDebInfo|arm64.Build.0 = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.RelWithDebInfo|x64.Build.0 = Release|x64 + {2FF51663-B837-42E3-9E00-31B1AB857920}.RelWithDebInfo|x86.ActiveCfg = Release|Win32 + {2FF51663-B837-42E3-9E00-31B1AB857920}.RelWithDebInfo|x86.Build.0 = Release|Win32 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Debug|ARM.ActiveCfg = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Debug|ARM.Build.0 = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Debug|arm64.ActiveCfg = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Debug|arm64.Build.0 = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Debug|x64.ActiveCfg = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Debug|x64.Build.0 = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Debug|x86.ActiveCfg = Debug|Win32 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Debug|x86.Build.0 = Debug|Win32 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.MinSizeRel|ARM.ActiveCfg = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.MinSizeRel|ARM.Build.0 = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.MinSizeRel|arm64.ActiveCfg = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.MinSizeRel|arm64.Build.0 = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.MinSizeRel|x64.ActiveCfg = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.MinSizeRel|x64.Build.0 = Debug|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.MinSizeRel|x86.ActiveCfg = Debug|Win32 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.MinSizeRel|x86.Build.0 = Debug|Win32 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Release|ARM.ActiveCfg = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Release|ARM.Build.0 = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Release|arm64.ActiveCfg = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Release|arm64.Build.0 = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Release|x64.ActiveCfg = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Release|x64.Build.0 = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Release|x86.ActiveCfg = Release|Win32 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.Release|x86.Build.0 = Release|Win32 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.RelWithDebInfo|ARM.ActiveCfg = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.RelWithDebInfo|ARM.Build.0 = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.RelWithDebInfo|arm64.ActiveCfg = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.RelWithDebInfo|arm64.Build.0 = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.RelWithDebInfo|x64.Build.0 = Release|x64 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.RelWithDebInfo|x86.ActiveCfg = Release|Win32 + {D3C781E2-F6E0-4334-BD33-5A514E22C366}.RelWithDebInfo|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp b/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp new file mode 100644 index 0000000..56985c2 --- /dev/null +++ b/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp @@ -0,0 +1,15 @@ +#include "CppUnitTest.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace ServerIntegrationTests +{ + TEST_CLASS(EstablishConnection) + { + public: + + TEST_METHOD(ValidateConnection) + { + } + }; +} diff --git a/ServerIntegrationTests/ServerIntegrationTests.vcxproj b/ServerIntegrationTests/ServerIntegrationTests.vcxproj new file mode 100644 index 0000000..02763a2 --- /dev/null +++ b/ServerIntegrationTests/ServerIntegrationTests.vcxproj @@ -0,0 +1,172 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + {2FF51663-B837-42E3-9E00-31B1AB857920} + Win32Proj + ServerIntegrationTests + 10.0 + NativeUnitTestProject + + + + DynamicLibrary + true + v143 + Unicode + false + + + DynamicLibrary + false + v143 + true + Unicode + false + + + DynamicLibrary + true + v143 + Unicode + false + + + DynamicLibrary + false + v143 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + _DEBUG;%(PreprocessorDefinitions) + true + pch.h + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + pch.h + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + true + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Use + Level3 + true + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + NDEBUG;%(PreprocessorDefinitions) + true + pch.h + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + + + + {f11a6291-405c-4a94-ab56-f0027fdd4b4d} + + + {ffa917e0-c449-48c7-bdf3-9877cbd96b46} + + + + + + \ No newline at end of file diff --git a/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters b/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters new file mode 100644 index 0000000..eab95fa --- /dev/null +++ b/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/ServerUnitTest/ConnectionUnitTest.cpp b/ServerUnitTest/ConnectionUnitTest.cpp deleted file mode 100644 index 90af926..0000000 --- a/ServerUnitTest/ConnectionUnitTest.cpp +++ /dev/null @@ -1,251 +0,0 @@ -#include "CppUnitTest.h" - -#include -#include -#include -#include -#include -#include -#include - - -#include "Connection.h" -#include "Coroutine.h" -#include "Server.h" - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; -using namespace std::chrono_literals; - -namespace ServerUnitTest -{ - TEST_CLASS(ConnectionUnitTest) - { - public: - void StartServer() - { - serverThread = std::jthread{ [this]() { server.Run("45321"); } }; - - std::unique_lock lock(mutex); - server.m_cv.wait(lock, [this] { return !server.m_stop; }); - - lock.unlock(); - lock.release(); - } - - std::shared_ptr CreateConnection() - { - addrinfo hints{}; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - addrinfo* result = nullptr; - int iResult = getaddrinfo("localhost", "45321", &hints, &result); - Assert::AreEqual(iResult, 0, L"Can't get address info"); - - SOCKET sendingSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); - Assert::AreNotEqual(sendingSocket, INVALID_SOCKET, L"Can't start test socket"); - - iResult = connect(sendingSocket, result->ai_addr, static_cast(result->ai_addrlen)); - Assert::AreEqual(iResult, 0, L"Can't connect to the server"); - - freeaddrinfo(result); - - return std::make_shared(sendingSocket, server, false); - } - - AsyncTask ConnectClient(std::shared_ptr client, std::string username) const - { - std::vector token = co_await client->ReceiveRawMessage(sizeof(uint64_t)); - *std::bit_cast(token.data()) ^= 0xF007CAFEC0C0CA7E; - co_await client->SendRawMessage(token); - - // Hello - std::shared_ptr message = co_await client->ReceiveMessage(); - - message = std::make_shared(); - // Hello - co_await client->SendMessage(message); - // ACK - message = co_await client->ReceiveMessage(); - - message = std::make_shared(); - std::dynamic_pointer_cast(message)->SetUsername(username); - - // Identify - co_await client->SendMessage(message); - // ACK - message = co_await client->ReceiveMessage(); - } - - Server server; - std::jthread serverThread; - std::mutex mutex; - - TEST_METHOD(EstablishConnection) - { - StartServer(); - - std::condition_variable cv; - bool finished = false; - - [&]() -> AsyncTask { - std::shared_ptr clientConnection = CreateConnection(); - - co_await SwitchThread(clientConnection->GetThread()); - - - - std::vector token = co_await clientConnection->ReceiveRawMessage(sizeof(uint64_t)); - Assert::AreEqual(token.size(), sizeof(uint64_t), L"Wrong token size"); - - *std::bit_cast(token.data()) ^= 0xF007CAFEC0C0CA7E; - - co_await clientConnection->SendRawMessage(token); - - - - - std::shared_ptr message = co_await clientConnection->ReceiveMessage(); - Assert::AreEqual(static_cast(message->header.messageType), - static_cast(SocketMessages::MessageType::HelloMessage), - L"Wrong message type received instead of Hello"); - - - - - message = std::make_shared(); - uint64_t expectedId = message->header.messageId; - - co_await clientConnection->SendMessage(message); - message = co_await clientConnection->ReceiveMessage(); - Assert::AreEqual(static_cast(message->header.messageType), - static_cast(SocketMessages::MessageType::AcknowledgeMessage), - L"Wrong message type instead of ACK"); - - uint64_t actualId = - std::dynamic_pointer_cast(message) - ->GetAcknowledgedMessageId(); - Assert::AreEqual(expectedId, actualId, L"Wrong message id received"); - - - - - message = std::make_shared(); - std::dynamic_pointer_cast(message)->SetUsername("Username"); - expectedId = message->header.messageId; - - co_await clientConnection->SendMessage(message); - message = co_await clientConnection->ReceiveMessage(); - Assert::AreEqual(static_cast(message->header.messageType), - static_cast(SocketMessages::MessageType::AcknowledgeMessage), - L"Wrong message type instead of ACK"); - - actualId = std::dynamic_pointer_cast(message) - ->GetAcknowledgedMessageId(); - Assert::AreEqual(expectedId, actualId, L"Wrong message id received"); - - - - closesocket(clientConnection->GetSocket()); - shutdown(clientConnection->GetSocket(), SD_BOTH); - - finished = true; - cv.notify_one(); - - co_return; - }(); - - { - std::unique_lock lock{ mutex }; - cv.wait(lock, [&finished] { return finished; }); - } - - server.Stop(); - } - - TEST_METHOD(SendChatMessage) - { - StartServer(); - - std::condition_variable cv; - std::pair finished = { false, false }; - std::pair connected = { false, false }; - - std::string chatContent = "Hello world"; - std::string senderUsername = "Sender"; - - std::mutex readyMutex; - - - - [&]() -> AsyncTask { - auto receiverConnection = CreateConnection(); - - co_await SwitchThread(receiverConnection->GetThread()); - - co_await ConnectClient(receiverConnection, "Receiver"); - - connected.second = true; - cv.notify_all(); - - auto&& message = co_await receiverConnection->ReceiveMessage(); - Assert::AreEqual(static_cast(message->header.messageType), - static_cast(SocketMessages::MessageType::SendChatMessage), - L"Wrong message type received instead of SendChatMessage"); - Assert::AreEqual(std::dynamic_pointer_cast(message)->GetChatMessage(), - chatContent, - L"Wrong message received"); - Assert::AreEqual(std::dynamic_pointer_cast(message)->GetAuthorUsername(), - senderUsername, - L"Wrong message received"); - - - closesocket(receiverConnection->GetSocket()); - shutdown(receiverConnection->GetSocket(), SD_BOTH); - - finished.second = true; - cv.notify_all(); - }(); - - - - [&]() -> AsyncTask { - auto senderConnection = CreateConnection(); - - co_await SwitchThread(senderConnection->GetThread()); - - co_await ConnectClient(senderConnection, senderUsername); - - { - std::unique_lock lock{ readyMutex }; - cv.wait(lock, [&connected] { return connected.second; }); - } - - auto message = std::make_shared(); - message->SetChatMessage(chatContent); - co_await senderConnection->SendMessage(message); - - auto&& Ack = co_await senderConnection->ReceiveMessage(); - Assert::AreEqual(static_cast(Ack->header.messageType), - static_cast(SocketMessages::MessageType::AcknowledgeMessage), - L"Wrong message type received instead of ACK"); - - closesocket(senderConnection->GetSocket()); - shutdown(senderConnection->GetSocket(), SD_BOTH); - - finished.first = true; - cv.notify_all(); - }(); - - - - { - std::unique_lock lock{ mutex }; - cv.wait(lock, [&finished] { return finished.first && finished.second; }); - } - - server.Stop(); - } - }; -} \ No newline at end of file diff --git a/ServerUnitTest/ServerUnitTest.vcxproj b/ServerUnitTest/ServerUnitTest.vcxproj index 835a21c..74d009b 100644 --- a/ServerUnitTest/ServerUnitTest.vcxproj +++ b/ServerUnitTest/ServerUnitTest.vcxproj @@ -168,7 +168,6 @@ - From bdbc7d77166fa6a3bbe1c334eebd494e77e12819 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Sat, 25 Mar 2023 12:33:37 +0100 Subject: [PATCH 024/203] Fix SendChatMessage able to parse wrong messages --- Server/SendChatMessage.h | 4 ++++ ServerUnitTest/ParseMessageUnitTests.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Server/SendChatMessage.h b/Server/SendChatMessage.h index 21f399f..cfa3dfc 100644 --- a/Server/SendChatMessage.h +++ b/Server/SendChatMessage.h @@ -26,6 +26,10 @@ namespace SocketMessages std::memcpy(std::bit_cast(&m_chatMessageLength), buffer.data(), sizeof(m_chatMessageLength)); + + if (buffer.size() != sizeof(m_chatMessageLength) + m_chatMessageLength) + return false; + m_chatMessage = std::string(buffer.begin() + sizeof(m_chatMessageLength), buffer.begin() + sizeof(m_chatMessageLength) + m_chatMessageLength); diff --git a/ServerUnitTest/ParseMessageUnitTests.cpp b/ServerUnitTest/ParseMessageUnitTests.cpp index 6ccf581..f8f7b3a 100644 --- a/ServerUnitTest/ParseMessageUnitTests.cpp +++ b/ServerUnitTest/ParseMessageUnitTests.cpp @@ -72,7 +72,7 @@ namespace ServerUnitTest TEST_METHOD(ParseIdentifyMessage) { std::string username = "Username"; - + SocketMessages::IdentifyMessage identifyMessage{}; identifyMessage.SetUsername(username); @@ -118,10 +118,10 @@ namespace ServerUnitTest TEST_METHOD(ParseSendChatMessage) { - std::string chatMessage = "Message"; + std::string validChatMessage = "Message"; SocketMessages::SendChatMessage sendChatMessage{}; - sendChatMessage.SetChatMessage(chatMessage); + sendChatMessage.SetChatMessage(validChatMessage); std::vector messageBuffer = sendChatMessage.Serialize(); std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); @@ -129,7 +129,7 @@ namespace ServerUnitTest Assert::IsFalse(sendChatMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); Assert::IsTrue(sendChatMessage.ParseBody(buffer), L"Valid data can't be parsed"); - Assert::AreEqual(chatMessage, sendChatMessage.GetChatMessage(), L"Chat message is wrong"); + Assert::AreEqual(validChatMessage, sendChatMessage.GetChatMessage(), L"Chat message is wrong"); } }; } From 3ce47cc61c831e12ad522ce43484912372ae77b1 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:34:38 +0200 Subject: [PATCH 025/203] Add server integration tests --- .../EstablishConnectionIntegrationTests.cpp | 68 ++++++++++++++++++- .../ServerIntegrationTests.vcxproj | 20 +++--- ServerUnitTest/ServerUnitTest.vcxproj.filters | 3 - 3 files changed, 78 insertions(+), 13 deletions(-) diff --git a/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp b/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp index 56985c2..ad59eb9 100644 --- a/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp +++ b/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp @@ -1,5 +1,11 @@ #include "CppUnitTest.h" +#include + +#include "Connection.h" +#include "Coroutine.h" +#include "Message.h" + using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace ServerIntegrationTests @@ -7,9 +13,67 @@ namespace ServerIntegrationTests TEST_CLASS(EstablishConnection) { public: - - TEST_METHOD(ValidateConnection) + + + + TEST_METHOD(EstablishSingleConnection) { + std::mutex mutex; + + [&mutex]() -> AsyncTask + { + ClientConnection client; + + Assert::IsTrue(client.Connect()); + + std::lock_guard lock{ mutex }; + + + + + std::vector validationToken = co_await client.ReceiveRawMessage(sizeof(uint64_t)); + + Assert::AreEqual(sizeof(uint64_t), validationToken.size(), L"Wrong received token size"); + + *std::bit_cast(validationToken.data()) ^= 0xF007CAFEC0C0CACA; + + co_await client.SendRawMessage(validationToken); + + std::shared_ptr validationResponse = co_await client.ReceiveMessage(); + + Assert::AreEqual(static_cast(validationResponse->header.messageType), + static_cast(SocketMessages::MessageType::HelloMessage), + L"Wrong message type received after validation"); + + + + + auto helloMessage = std::make_shared(); + + co_await client.SendMessage(helloMessage); + + std::shared_ptr helloResponse = co_await client.ReceiveMessage(); + + Assert::AreEqual(static_cast(helloResponse->header.messageType), + static_cast(SocketMessages::MessageType::AcknowledgeMessage), + L"Wrong message type received after hello"); + + + + + auto identifyMessage = std::make_shared(); + identifyMessage->SetUsername("Username"); + + co_await client.SendMessage(identifyMessage); + + std::shared_ptr identifyResponse = co_await client.ReceiveMessage(); + + Assert::AreEqual(static_cast(identifyResponse->header.messageType), + static_cast(SocketMessages::MessageType::AcknowledgeMessage), + L"Wrong message type received after hello"); + }(); + + std::lock_guard lock{ mutex }; } }; } diff --git a/ServerIntegrationTests/ServerIntegrationTests.vcxproj b/ServerIntegrationTests/ServerIntegrationTests.vcxproj index 02763a2..d465668 100644 --- a/ServerIntegrationTests/ServerIntegrationTests.vcxproj +++ b/ServerIntegrationTests/ServerIntegrationTests.vcxproj @@ -89,13 +89,14 @@ - Use + NotUsing Level3 true - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + $(SolutionDir)Server;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) _DEBUG;%(PreprocessorDefinitions) true pch.h + stdcpplatest Windows @@ -104,13 +105,14 @@ - Use + NotUsing Level3 true - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + $(SolutionDir)Server;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;%(PreprocessorDefinitions) true pch.h + stdcpplatest Windows @@ -119,15 +121,16 @@ - Use + NotUsing Level3 true true true - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + $(SolutionDir)Server;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;%(PreprocessorDefinitions) true pch.h + stdcpplatest Windows @@ -138,15 +141,16 @@ - Use + NotUsing Level3 true true true - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + $(SolutionDir)Server;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) NDEBUG;%(PreprocessorDefinitions) true pch.h + stdcpplatest Windows diff --git a/ServerUnitTest/ServerUnitTest.vcxproj.filters b/ServerUnitTest/ServerUnitTest.vcxproj.filters index 14da80a..6a76d6d 100644 --- a/ServerUnitTest/ServerUnitTest.vcxproj.filters +++ b/ServerUnitTest/ServerUnitTest.vcxproj.filters @@ -21,8 +21,5 @@ Source Files - - Source Files - \ No newline at end of file From 406ed5742d9ff07039124e2e7fdb8abc7cccc716 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:39:12 +0200 Subject: [PATCH 026/203] Differenciate server and client connections for ease of use while testing --- Server/ClientConnection.cpp | 47 +++++++++++ Server/Connection.cpp | 154 ++-------------------------------- Server/Connection.h | 53 +++++++----- Server/Server.cpp | 2 +- Server/Server.h | 2 +- Server/Server.vcxproj | 12 +++ Server/Server.vcxproj.filters | 21 +++-- Server/ServerConnection.cpp | 144 +++++++++++++++++++++++++++++++ Server/User.cpp | 2 +- 9 files changed, 259 insertions(+), 178 deletions(-) create mode 100644 Server/ClientConnection.cpp create mode 100644 Server/ServerConnection.cpp diff --git a/Server/ClientConnection.cpp b/Server/ClientConnection.cpp new file mode 100644 index 0000000..8b04d42 --- /dev/null +++ b/Server/ClientConnection.cpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#include "Connection.h" +#include "Snowflake.h" + +ClientConnection::ClientConnection() +{} + +bool ClientConnection::Connect() +{ + addrinfo hints{}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + addrinfo* result = nullptr; + int i = getaddrinfo("localhost", "45321", &hints, &result); + if (i) + { + WSACleanup(); + return false; + } + + m_socket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (m_socket == INVALID_SOCKET) + { + freeaddrinfo(result); + WSACleanup(); + return false; + } + + i = connect(m_socket, result->ai_addr, static_cast(result->ai_addrlen)); + if (i == SOCKET_ERROR) + { + closesocket(m_socket); + m_socket = INVALID_SOCKET; + return false; + } + + freeaddrinfo(result); + + return true; +} \ No newline at end of file diff --git a/Server/Connection.cpp b/Server/Connection.cpp index 0b55180..67b3433 100644 --- a/Server/Connection.cpp +++ b/Server/Connection.cpp @@ -1,7 +1,8 @@ #include #include #include -#include +#include +#include #include #include "Connection.h" @@ -12,20 +13,9 @@ - -Connection::Connection(SOCKET socket, Server& server, bool serverConnection) - : m_server{ server } - , m_socket{ socket } - , m_serverConnection{ serverConnection } -{ - std::cout << "New client trying to connect: " << std::dec << m_id << std::endl; -} - - - Connection::~Connection() { - std::cout << "Client disconnected: " << std::dec << m_id << std::endl; + std::cout << "Closing client connection: " << m_id << std::endl; if (m_socket != INVALID_SOCKET) { @@ -36,38 +26,6 @@ Connection::~Connection() -AsyncTask Connection::Listen() -{ - co_await SwitchThread(m_thread); - - if (!(co_await EstablishConnection())) - { - if (!m_disconnecting) - { - m_disconnecting = true; - m_server.DisconnectClient(m_id); - } - co_return; - } - - co_await m_server.OnConnect(shared_from_this()); - - while (true) - { - auto&& message = co_await ReceiveMessage(); - - if (message->header.messageType == SocketMessages::MessageType::InvalidMessage) - { - m_server.DisconnectClient(m_id); - co_return; - } - - m_server.OnMessage(shared_from_this(), message); - } -} - - - AsyncOperation> Connection::ReceiveRawMessage(uint64_t const& bufferSize) const { std::vector buffer(bufferSize, 0); @@ -80,7 +38,7 @@ AsyncOperation> Connection::ReceiveRawMessage(uint64_t cons { int error = WSAGetLastError(); - if (error == WSAECONNRESET || error == WSAECONNABORTED && m_serverConnection) + if (error == WSAECONNRESET || error == WSAECONNABORTED) { co_return buffer; } @@ -176,7 +134,8 @@ AsyncOperation> Connection::ReceiveMess message->header = header; - m_lastMessageId = message->header.messageId; + if (dynamic_cast(this) != nullptr) + co_await ((ServerConnection*)this)->SendAck(header.messageId); co_return message; } @@ -189,104 +148,3 @@ AsyncTask Connection::SendMessage(std::shared_ptr const co_await SendRawMessage(buffer); } - - - -AsyncOperation Connection::EstablishConnection() -{ - if (!(co_await ValidateConnection())) - { - std::cout << " Client failed validation: " << std::dec << m_id << std::endl; - co_return false; - } - - std::cout << " Client passed validation: " << std::dec << m_id << std::endl; - - if (!(co_await CheckVersion())) - { - std::cout << " Client failed version check: " << std::dec << m_id << std::endl; - co_return false; - } - - co_await SendAck(); - - std::cout << " Client passed version check: " << std::dec << m_id << std::endl; - - if (!(co_await Identify())) - { - std::cout << " Client failed identify: " << std::dec << m_id << std::endl; - co_return false; - } - - co_await SendAck(); - - std::cout << " Client successfully identified in as \"" << m_user->m_username << "\": " << std::dec << m_id << std::endl; - - co_return true; -} - - - -AsyncOperation Connection::CheckVersion() -{ - co_await SendMessage(std::make_shared()); - - auto&& hello = co_await ReceiveMessage(); - if (hello->header.messageType != SocketMessages::MessageType::HelloMessage) - co_return false; - - auto version = std::dynamic_pointer_cast(hello)->GetVersion(); - if (version != CurrentVersion) - co_return false; - - co_return true; -} - - - -AsyncOperation Connection::Identify() -{ - auto&& message = co_await ReceiveMessage(); - - if (message->header.messageType != SocketMessages::MessageType::IdentifyMessage) - co_return false; - - auto&& identifyMessage = std::dynamic_pointer_cast(message); - - m_user->m_username = identifyMessage->GetUsername(); - - co_return true; -} - - - -AsyncOperation Connection::ValidateConnection() const -{ - std::random_device device; - std::default_random_engine engine(device()); - std::uniform_int_distribution distribution(0, -1); - uint64_t key = distribution(engine); - - std::vector keyBuffer(std::bit_cast(&key), std::bit_cast(&key) + sizeof(uint64_t)); - - std::cout << " Sending key " << std::hex << key << " to client " << std::dec << m_id << std::endl; - std::cout << " Client should answer " << std::hex << (key ^ 0xF007CAFEC0C0CA7E) << std::endl; - co_await SendRawMessage(keyBuffer); - - auto&& response = co_await ReceiveRawMessage(sizeof(uint64_t)); - if (response.size() != sizeof(uint64_t)) - co_return false; - - key ^= 0xF007CAFEC0C0CA7E; - keyBuffer.assign(std::bit_cast(&key), std::bit_cast(&key) + sizeof(uint64_t)); - - if (std::ranges::equal(keyBuffer, response)) - co_return true; - co_return false; -} - -AsyncTask Connection::SendAck() const -{ - auto message = std::make_shared(m_lastMessageId); - co_await SendMessage(message); -} diff --git a/Server/Connection.h b/Server/Connection.h index 71a71e8..b9b3095 100644 --- a/Server/Connection.h +++ b/Server/Connection.h @@ -2,13 +2,13 @@ #include #include +#include #include #include #include "Coroutine.h" #include "SocketMessages.h" #include "Snowflake.h" -#include "User.h" #undef SendMessage @@ -17,10 +17,7 @@ class Server; class Connection : public std::enable_shared_from_this { public: - Connection(SOCKET socket, Server& server, bool serverConnection = true); - ~Connection(); - - AsyncTask Listen(); + virtual ~Connection(); AsyncOperation> ReceiveMessage(); AsyncTask SendMessage(std::shared_ptr const& message) const; @@ -29,29 +26,43 @@ class Connection : public std::enable_shared_from_this AsyncTask SendRawMessage(std::vector const& buffer) const; [[nodiscard]] constexpr Snowflake const& GetId() const noexcept { return m_id; } - [[nodiscard]] constexpr std::jthread& GetThread() noexcept { return m_thread; } - [[nodiscard]] constexpr std::shared_ptr const& GetUser() const noexcept { return m_user; } + [[nodiscard]] constexpr User& GetUser() noexcept { return m_user; } [[nodiscard]] constexpr SOCKET const& GetSocket() const noexcept { return m_socket; } - [[nodiscard]] constexpr bool const& IsDisconnecting() const noexcept { return m_disconnecting; } - constexpr void SetDisconnecting(bool const& disconnecting) noexcept { m_disconnecting = disconnecting; } +protected: + Snowflake m_id; + SOCKET m_socket{ INVALID_SOCKET }; + User m_user{}; +}; - AsyncTask SendAck() const; +class ClientConnection : public Connection +{ + friend class Connection; -private: + public: + ClientConnection(); + + bool Connect(); +}; + +class ServerConnection : public Connection +{ + friend class Connection; + +public: + ServerConnection(SOCKET& socket, Server& server); + + AsyncTask Listen(); + + [[nodiscard]] constexpr std::jthread& GetListenThread() noexcept { return m_listenThread; } + AsyncTask SendAck(Snowflake id) const; +private: AsyncOperation EstablishConnection(); + AsyncOperation ValidateConnection(); AsyncOperation CheckVersion(); AsyncOperation Identify(); - AsyncOperation ValidateConnection() const; - Snowflake m_id; - Snowflake m_lastMessageId; Server& m_server; - SOCKET m_socket; - std::shared_ptr m_user{ std::make_shared() }; - std::jthread m_thread; - - bool m_disconnecting{ false }; - bool m_serverConnection{ true }; -}; + std::jthread m_listenThread; +}; \ No newline at end of file diff --git a/Server/Server.cpp b/Server/Server.cpp index 06f450e..279a6f1 100644 --- a/Server/Server.cpp +++ b/Server/Server.cpp @@ -43,7 +43,7 @@ void Server::Run(std::string_view const& port) throw ServerException{ "Failed to accept client: " + WSAGetLastError() }; } - auto client = std::make_shared(clientSocket, *this); + auto client = std::make_shared(clientSocket, *this); client->Listen(); std::unique_lock lock{ m_mutateClients }; diff --git a/Server/Server.h b/Server/Server.h index 386f5fe..8902c7e 100644 --- a/Server/Server.h +++ b/Server/Server.h @@ -35,7 +35,7 @@ class Server bool InitSocket(std::string_view const& port); - std::deque> m_clients; + std::unordered_map> m_clients; bool m_stop = true; SOCKET m_socket = INVALID_SOCKET; diff --git a/Server/Server.vcxproj b/Server/Server.vcxproj index 1fb7db8..d252ed1 100644 --- a/Server/Server.vcxproj +++ b/Server/Server.vcxproj @@ -70,6 +70,18 @@ + + false + + + false + + + false + + + false + Level3 diff --git a/Server/Server.vcxproj.filters b/Server/Server.vcxproj.filters index ddc45cf..0c1a9df 100644 --- a/Server/Server.vcxproj.filters +++ b/Server/Server.vcxproj.filters @@ -19,14 +19,14 @@ {3fd1da76-1f98-4320-bb32-42a89e3c57aa} + + {84a936c2-ad1d-49ea-b68c-43529ea0bc69} + Source Files - - Source Files - Header Files @@ -45,14 +45,20 @@ Source Files + + Source Files + + + Source Files\Connections + + + Source Files\Connections + Header Files - - Header Files - Header Files @@ -86,5 +92,8 @@ Header Files\SocketMessages + + Header Files + \ No newline at end of file diff --git a/Server/ServerConnection.cpp b/Server/ServerConnection.cpp new file mode 100644 index 0000000..f0e2ce4 --- /dev/null +++ b/Server/ServerConnection.cpp @@ -0,0 +1,144 @@ +#include +#include +#include +#include + +#include "Connection.h" +#include "Server.h" + + + +ServerConnection::ServerConnection(SOCKET& clientSocket, Server& server) + : m_server{ server } +{ + m_socket = clientSocket; + + std::cout << "New client connection opened: " << m_id << std::endl; +} + + + +AsyncTask ServerConnection::Listen() +{ + co_await SwitchThread(m_listenThread); + + if (!(co_await EstablishConnection())) + { + m_server.DisconnectClient(m_id); + co_return; + } + + co_await m_server.OnConnect(shared_from_this()); + + while (true) + { + auto&& message = co_await ReceiveMessage(); + + if (message->header.messageType == SocketMessages::MessageType::InvalidMessage) + { + m_server.DisconnectClient(m_id); + co_return; + } + + m_server.OnMessage(shared_from_this(), message); + } +} + + + +AsyncOperation ServerConnection::EstablishConnection() +{ + if (!(co_await ValidateConnection())) + { + std::cout << " Client failed validation: " << m_id << std::endl; + co_return false; + } + + std::cout << " Client passed validation: " << m_id << std::endl; + + if (!(co_await CheckVersion())) + { + std::cout << " Client failed version check: " << m_id << std::endl; + co_return false; + } + + std::cout << " Client passed version check: " << m_id << std::endl; + + if (!(co_await Identify())) + { + std::cout << " Client failed identify: " << m_id << std::endl; + co_return false; + } + + std::cout << " Client successfully identified in as \"" << m_user.GetUsername() << "\": " << std::dec << m_id << std::endl; + + co_return true; +} + + + +AsyncOperation ServerConnection::ValidateConnection() +{ + std::random_device device; + std::default_random_engine engine(device()); + std::uniform_int_distribution distribution(0, -1); + uint64_t key = distribution(engine); + + std::vector keyBuffer(std::bit_cast(&key), std::bit_cast(&key) + sizeof(uint64_t)); + + std::cout << " Sending key " << std::hex << key << " to client " << std::dec << m_id << std::endl; + std::cout << " Client should answer " << std::hex << (key ^ 0xF007CAFEC0C0CACA) << std::endl; + co_await SendRawMessage(keyBuffer); + + auto&& response = co_await ReceiveRawMessage(sizeof(uint64_t)); + if (response.size() != sizeof(uint64_t)) + co_return false; + + key ^= 0xF007CAFEC0C0CACA; + keyBuffer.assign(std::bit_cast(&key), std::bit_cast(&key) + sizeof(uint64_t)); + + if (std::ranges::equal(keyBuffer, response)) + co_return true; + co_return false; +} + + + +AsyncOperation ServerConnection::CheckVersion() +{ + co_await SendMessage(std::make_shared()); + + auto&& hello = co_await ReceiveMessage(); + if (hello->header.messageType != SocketMessages::MessageType::HelloMessage) + co_return false; + + auto version = std::dynamic_pointer_cast(hello)->GetVersion(); + if (version != CurrentVersion) + co_return false; + + co_return true; +} + + + +AsyncOperation ServerConnection::Identify() +{ + auto&& message = co_await ReceiveMessage(); + + if (message->header.messageType != SocketMessages::MessageType::IdentifyMessage) + co_return false; + + auto&& identifyMessage = std::dynamic_pointer_cast(message); + + m_user.SetUsername(identifyMessage->GetUsername()); + + co_return true; +} + + + +AsyncTask ServerConnection::SendAck(Snowflake id) const +{ + auto message = std::make_shared(id); + co_await SendMessage(message); +} diff --git a/Server/User.cpp b/Server/User.cpp index 1002b2e..306b307 100644 --- a/Server/User.cpp +++ b/Server/User.cpp @@ -1,3 +1,3 @@ #include "User.h" -#include "Connection.h" +#include "Connection.h" \ No newline at end of file From 29781ccf39ed36d7a06e6a2004f23fc8452b5e1c Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:40:04 +0200 Subject: [PATCH 027/203] Increase consistency --- Server/Server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Server/Server.cpp b/Server/Server.cpp index 279a6f1..c9768ed 100644 --- a/Server/Server.cpp +++ b/Server/Server.cpp @@ -15,7 +15,7 @@ void Server::Run(std::string_view const& port) { if (!InitSocket(port)) { - throw ServerException{ "Failed to initialize socket: " + WSAGetLastError() }; + throw ServerException{ "Failed to initialize socket: " + std::to_string(WSAGetLastError()) }; } std::cout << "Pine - version " << std::hex << CurrentVersion << "\nServer Listening" << std::endl; @@ -40,7 +40,7 @@ void Server::Run(std::string_view const& port) { closesocket(m_socket); WSACleanup(); - throw ServerException{ "Failed to accept client: " + WSAGetLastError() }; + throw ServerException{ "Failed to accept client: " + std::to_string(WSAGetLastError()) }; } auto client = std::make_shared(clientSocket, *this); From 32d6bd119bd2706454c822a95fb95e3bc1f3084e Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:40:17 +0200 Subject: [PATCH 028/203] Add more conversions for snowflake ID --- Server/Snowflake.h | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Server/Snowflake.h b/Server/Snowflake.h index 67725a2..020da11 100644 --- a/Server/Snowflake.h +++ b/Server/Snowflake.h @@ -19,7 +19,9 @@ struct Snowflake Value.sequence = 0; } - explicit Snowflake(uint64_t const& id) + template + requires std::is_integral_v + explicit Snowflake(T const& id) { Value.timestamp = id >> 22; Value.workerId = (id >> 17) & 0x1F; @@ -35,6 +37,18 @@ struct Snowflake uint64_t sequence : 12; } Value; + template + requires std::is_integral_v + Snowflake operator =(T const& id) + { + return Snowflake(id); + } + + bool operator !=(Snowflake const& other) const + { + return !(*this == other); + } + bool operator ==(Snowflake const& other) const { return Value.timestamp == other.Value.timestamp @@ -43,6 +57,13 @@ struct Snowflake && Value.sequence == other.Value.sequence; } + template + requires std::is_integral_v + bool operator ==(T const& other) const + { + return Snowflake(other) == *this; + } + template requires std::is_integral_v explicit(false) constexpr operator T() const @@ -59,4 +80,4 @@ struct Snowflake } static inline uint64_t lastMillisecond = 0; -}; +}; \ No newline at end of file From 7cbd8520a70f8cf2315f6739e2cd54b198ba37ef Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:41:37 +0200 Subject: [PATCH 029/203] Add SetUsername function to user class --- Server/User.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Server/User.h b/Server/User.h index 94e24cd..3444f62 100644 --- a/Server/User.h +++ b/Server/User.h @@ -3,8 +3,6 @@ #include #include -class Connection; - class User : public std::enable_shared_from_this { public: @@ -12,9 +10,8 @@ class User : public std::enable_shared_from_this ~User() = default; [[nodiscard]] constexpr std::string const& GetUsername() const noexcept { return m_username; } + constexpr void SetUsername(std::string_view const& username) { m_username = username; } private: std::string m_username{ "" }; - - friend class Connection; }; From 78a374f6403464bbe4c327fd19a2336a01131231 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:42:27 +0200 Subject: [PATCH 030/203] Don't use pointers for user storage --- Server/Server.vcxproj | 2 ++ Server/ServerOnMessage.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Server/Server.vcxproj b/Server/Server.vcxproj index d252ed1..bc34d0d 100644 --- a/Server/Server.vcxproj +++ b/Server/Server.vcxproj @@ -151,10 +151,12 @@ + + diff --git a/Server/ServerOnMessage.cpp b/Server/ServerOnMessage.cpp index 385ec75..15db774 100644 --- a/Server/ServerOnMessage.cpp +++ b/Server/ServerOnMessage.cpp @@ -31,7 +31,7 @@ AsyncTask Server::OnMessage(std::shared_ptr client, std::shared_ptr< { std::cout << " Received chat message from client: " << std::dec << client->GetId() << std::endl; auto receiveChat = std::make_shared(); - receiveChat->SetAuthorUsername(client->GetUser()->GetUsername()); + receiveChat->SetAuthorUsername(client->GetUser().GetUsername()); receiveChat->SetChatMessage(std::dynamic_pointer_cast(message)->GetChatMessage()); co_await client->SendAck(); From 0292e8e54eb13f1b1395456becb8884d9d83fde6 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:43:08 +0200 Subject: [PATCH 031/203] Adopt new strategy to delete ended connections --- Server/Server.cpp | 64 +++++++++++++++++++------------------- Server/Server.h | 15 +++++---- Server/ServerOnMessage.cpp | 4 +-- 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/Server/Server.cpp b/Server/Server.cpp index c9768ed..f617fd1 100644 --- a/Server/Server.cpp +++ b/Server/Server.cpp @@ -21,18 +21,13 @@ void Server::Run(std::string_view const& port) std::cout << "Pine - version " << std::hex << CurrentVersion << "\nServer Listening" << std::endl; m_stop = false; - bool firstLoop = true; + + DeleteClients(); while (!m_stop) { - if (firstLoop) - { - firstLoop = false; - m_cv.notify_one(); - } - SOCKET clientSocket = accept(m_socket, nullptr, nullptr); - + if (m_stop) break; @@ -46,8 +41,8 @@ void Server::Run(std::string_view const& port) auto client = std::make_shared(clientSocket, *this); client->Listen(); - std::unique_lock lock{ m_mutateClients }; - m_clients.push_back(std::move(client)); + std::unique_lock lock{ m_mutateClientsMutex }; + m_clients.insert({ client->GetId(), std::move(client) }); } std::cout << "Socket stops listening" << std::endl; @@ -61,45 +56,50 @@ void Server::Stop() for (auto const& client : m_clients) { - if (!client->IsDisconnecting()) - DisconnectClient(client->GetId()); + DisconnectClient(client.first); } - + shutdown(m_socket, SD_BOTH); closesocket(m_socket); WSACleanup(); - m_cv.notify_one(); } -AsyncTask Server::DisconnectClient(Snowflake clientId) +AsyncTask Server::DeleteClients() { - std::unique_lock lock{ m_mutateClients }; + co_await SwitchThread(m_deleteClientsThread); + + while (!m_stop) + { + std::unique_lock lock { m_deleteClientsMutex }; + m_deleteCliens.wait(lock); + m_clientsToDelete.clear(); + } +} - auto client = std::ranges::find_if(m_clients, [clientId](auto const& potentialClient) - { - return potentialClient->GetId() == clientId; - }); - if (client != m_clients.end() && !(*client)->IsDisconnecting()) - { - (*client)->SetDisconnecting(true); - closesocket((*client)->GetSocket()); - shutdown((*client)->GetSocket(), SD_BOTH); +AsyncTask Server::DisconnectClient(uint64_t clientId) +{ + std::unique_lock lock{ m_mutateClientsMutex }; - if ((*client)->GetThread().joinable()) - { - (*client)->GetThread().request_stop(); - (*client)->GetThread().join(); - } + auto client = m_clients.find(clientId); - m_clients.erase(client); + if (client == m_clients.end()) + { + co_return; } - m_cv.notify_all(); + closesocket(client->second->GetSocket()); + shutdown(client->second->GetSocket(), SD_BOTH); + + m_clientsToDelete.push_back(std::move(client->second)); + m_clients.erase(clientId); + + m_deleteCliens.notify_one(); + co_return; } diff --git a/Server/Server.h b/Server/Server.h index 8902c7e..2007b2f 100644 --- a/Server/Server.h +++ b/Server/Server.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include #include @@ -22,22 +22,25 @@ class Server void Run(std::string_view const& port = "80"); void Stop(); - AsyncTask DisconnectClient(Snowflake clientId); + AsyncTask DisconnectClient(uint64_t clientId); AsyncTask MessageClient(std::shared_ptr const& client, std::shared_ptr const& message) const; AsyncTask OnConnect(std::shared_ptr const& client) const; AsyncTask OnMessage(std::shared_ptr client, std::shared_ptr message); -#ifndef MS_CPP_UNITTESTFRAMEWORK private: -#endif - std::condition_variable m_cv; - std::mutex m_mutateClients; + AsyncTask DeleteClients(); + + std::condition_variable m_deleteCliens; + std::mutex m_deleteClientsMutex; + std::mutex m_mutateClientsMutex; bool InitSocket(std::string_view const& port); std::unordered_map> m_clients; + std::vector> m_clientsToDelete; bool m_stop = true; SOCKET m_socket = INVALID_SOCKET; std::jthread m_thread; + std::jthread m_deleteClientsThread; }; diff --git a/Server/ServerOnMessage.cpp b/Server/ServerOnMessage.cpp index 15db774..0c15083 100644 --- a/Server/ServerOnMessage.cpp +++ b/Server/ServerOnMessage.cpp @@ -34,11 +34,9 @@ AsyncTask Server::OnMessage(std::shared_ptr client, std::shared_ptr< receiveChat->SetAuthorUsername(client->GetUser().GetUsername()); receiveChat->SetChatMessage(std::dynamic_pointer_cast(message)->GetChatMessage()); - co_await client->SendAck(); - for (auto const& iteratedClient : m_clients) { - if (iteratedClient->GetId() == client->GetId()) + if (client->GetId() == iteratedClient.first) continue; co_await client->SendMessage(receiveChat); } From e6a827e15cb0f449bbb14db9a2fdbcdb1054e98a Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Thu, 30 Mar 2023 20:21:24 +0200 Subject: [PATCH 032/203] Add utilitary function --- ServerIntegrationTests/EstablishConnection.h | 24 +++++++++++++++++++ .../EstablishConnectionIntegrationTests.cpp | 2 +- .../ServerIntegrationTests.vcxproj | 4 ++++ .../ServerIntegrationTests.vcxproj.filters | 8 +++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 ServerIntegrationTests/EstablishConnection.h diff --git a/ServerIntegrationTests/EstablishConnection.h b/ServerIntegrationTests/EstablishConnection.h new file mode 100644 index 0000000..317b256 --- /dev/null +++ b/ServerIntegrationTests/EstablishConnection.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Connection.h" + +namespace ServerIntegrationTests +{ + AsyncTask EstablishConnection(ClientConnection& client) + { + client.Connect(); + std::vector validationToken = co_await client.ReceiveRawMessage(sizeof(uint64_t)); + *std::bit_cast(validationToken.data()) ^= 0xF007CAFEC0C0CACA; + co_await client.SendRawMessage(validationToken); + std::shared_ptr validationResponse = co_await client.ReceiveMessage(); + + auto helloMessage = std::make_shared(); + co_await client.SendMessage(helloMessage); + std::shared_ptr helloResponse = co_await client.ReceiveMessage(); + + auto identifyMessage = std::make_shared(); + identifyMessage->SetUsername("Username"); + co_await client.SendMessage(identifyMessage); + std::shared_ptr identifyResponse = co_await client.ReceiveMessage(); + } +} \ No newline at end of file diff --git a/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp b/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp index ad59eb9..9e81fbd 100644 --- a/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp +++ b/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp @@ -10,7 +10,7 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace ServerIntegrationTests { - TEST_CLASS(EstablishConnection) + TEST_CLASS(EstablishConnectionIntegrationTests) { public: diff --git a/ServerIntegrationTests/ServerIntegrationTests.vcxproj b/ServerIntegrationTests/ServerIntegrationTests.vcxproj index d465668..1d5c2d1 100644 --- a/ServerIntegrationTests/ServerIntegrationTests.vcxproj +++ b/ServerIntegrationTests/ServerIntegrationTests.vcxproj @@ -160,6 +160,7 @@ + @@ -170,6 +171,9 @@ {ffa917e0-c449-48c7-bdf3-9877cbd96b46} + + + diff --git a/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters b/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters index eab95fa..68ad41a 100644 --- a/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters +++ b/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters @@ -18,5 +18,13 @@ Source Files + + Source Files + + + + + Header Files + \ No newline at end of file From 1083c3c342bc03c82d243ec2d309099671c90ad8 Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Thu, 30 Mar 2023 20:21:35 +0200 Subject: [PATCH 033/203] Add chat integration tests --- .../ChatIntegrationsTests.cpp | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 ServerIntegrationTests/ChatIntegrationsTests.cpp diff --git a/ServerIntegrationTests/ChatIntegrationsTests.cpp b/ServerIntegrationTests/ChatIntegrationsTests.cpp new file mode 100644 index 0000000..556ca9b --- /dev/null +++ b/ServerIntegrationTests/ChatIntegrationsTests.cpp @@ -0,0 +1,53 @@ +#include "CppUnitTest.h" + +#include + +#include "Connection.h" +#include "Coroutine.h" +#include "EstablishConnection.h" +#include "Message.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace ServerIntegrationTests +{ + TEST_CLASS(ChatIntegrationTests) + { + TEST_METHOD(SendChatMessage) + { + std::mutex mutex; + + [&mutex]() -> AsyncTask + { + std::lock_guard lock{ mutex }; + + ClientConnection sender; + ClientConnection receiver; + + co_await EstablishConnection(sender); + co_await EstablishConnection(receiver); + + auto sendChat = std::make_shared(); + sendChat->SetChatMessage("Hello world!"); + + co_await sender.SendMessage(sendChat); + + std::shared_ptr receivedChat = co_await receiver.ReceiveMessage(); + + Assert::AreEqual(static_cast(receivedChat->header.messageType), + static_cast(SocketMessages::MessageType::ReceiveChatMessage), + L"Receiver didn't receive message"); + + Assert::AreEqual(std::dynamic_pointer_cast(receivedChat)->GetChatMessage(), + std::string("Hello world!"), + L"Receiver didn't receive correct message"); + + Assert::AreEqual(std::dynamic_pointer_cast(receivedChat)->GetAuthorUsername(), + sender.GetUser().GetUsername(), + L"Received message from wrong username"); + }(); + + std::lock_guard lock{ mutex }; + } + }; +} \ No newline at end of file From 156e58e8a9e85de7a4b3d410cc336e21c152151d Mon Sep 17 00:00:00 2001 From: Jacquwes <38167139+Jacquwes@users.noreply.github.com> Date: Thu, 30 Mar 2023 20:21:50 +0200 Subject: [PATCH 034/203] Add cast --- Server/ServerOnMessage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/ServerOnMessage.cpp b/Server/ServerOnMessage.cpp index 0c15083..0d7d41b 100644 --- a/Server/ServerOnMessage.cpp +++ b/Server/ServerOnMessage.cpp @@ -38,7 +38,7 @@ AsyncTask Server::OnMessage(std::shared_ptr client, std::shared_ptr< { if (client->GetId() == iteratedClient.first) continue; - co_await client->SendMessage(receiveChat); + co_await std::dynamic_pointer_cast(client)->SendMessage(receiveChat); } break; From 84a2fd5b462cba74d42e548d2c64d8f808881104 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 17:56:26 +0200 Subject: [PATCH 035/203] Fix typo --- Server/Server.cpp | 4 ++-- Server/Server.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Server/Server.cpp b/Server/Server.cpp index f617fd1..cd9412e 100644 --- a/Server/Server.cpp +++ b/Server/Server.cpp @@ -73,7 +73,7 @@ AsyncTask Server::DeleteClients() while (!m_stop) { std::unique_lock lock { m_deleteClientsMutex }; - m_deleteCliens.wait(lock); + m_deleteClients.wait(lock); m_clientsToDelete.clear(); } } @@ -98,7 +98,7 @@ AsyncTask Server::DisconnectClient(uint64_t clientId) m_clients.erase(clientId); - m_deleteCliens.notify_one(); + m_deleteClients.notify_one(); co_return; } diff --git a/Server/Server.h b/Server/Server.h index 2007b2f..6c1ad54 100644 --- a/Server/Server.h +++ b/Server/Server.h @@ -30,7 +30,7 @@ class Server private: AsyncTask DeleteClients(); - std::condition_variable m_deleteCliens; + std::condition_variable m_deleteClients; std::mutex m_deleteClientsMutex; std::mutex m_mutateClientsMutex; From 627102c822c8b86c057642e66860269313228ba5 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 19:21:27 +0200 Subject: [PATCH 036/203] Move tests to their own directory --- .../ServerIntegrationTests}/ChatIntegrationsTests.cpp | 0 .../ServerIntegrationTests}/EstablishConnection.h | 0 .../EstablishConnectionIntegrationTests.cpp | 0 .../ServerIntegrationTests}/ServerIntegrationTests.vcxproj | 0 .../ServerIntegrationTests.vcxproj.filters | 0 .../ServerUnitTest}/ParseMessageUnitTests.cpp | 0 .../ServerUnitTest}/SerializeMessageUnitTests.cpp | 0 {ServerUnitTest => tests/ServerUnitTest}/ServerUnitTest.vcxproj | 0 .../ServerUnitTest}/ServerUnitTest.vcxproj.filters | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {ServerIntegrationTests => tests/ServerIntegrationTests}/ChatIntegrationsTests.cpp (100%) rename {ServerIntegrationTests => tests/ServerIntegrationTests}/EstablishConnection.h (100%) rename {ServerIntegrationTests => tests/ServerIntegrationTests}/EstablishConnectionIntegrationTests.cpp (100%) rename {ServerIntegrationTests => tests/ServerIntegrationTests}/ServerIntegrationTests.vcxproj (100%) rename {ServerIntegrationTests => tests/ServerIntegrationTests}/ServerIntegrationTests.vcxproj.filters (100%) rename {ServerUnitTest => tests/ServerUnitTest}/ParseMessageUnitTests.cpp (100%) rename {ServerUnitTest => tests/ServerUnitTest}/SerializeMessageUnitTests.cpp (100%) rename {ServerUnitTest => tests/ServerUnitTest}/ServerUnitTest.vcxproj (100%) rename {ServerUnitTest => tests/ServerUnitTest}/ServerUnitTest.vcxproj.filters (100%) diff --git a/ServerIntegrationTests/ChatIntegrationsTests.cpp b/tests/ServerIntegrationTests/ChatIntegrationsTests.cpp similarity index 100% rename from ServerIntegrationTests/ChatIntegrationsTests.cpp rename to tests/ServerIntegrationTests/ChatIntegrationsTests.cpp diff --git a/ServerIntegrationTests/EstablishConnection.h b/tests/ServerIntegrationTests/EstablishConnection.h similarity index 100% rename from ServerIntegrationTests/EstablishConnection.h rename to tests/ServerIntegrationTests/EstablishConnection.h diff --git a/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp b/tests/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp similarity index 100% rename from ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp rename to tests/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp diff --git a/ServerIntegrationTests/ServerIntegrationTests.vcxproj b/tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj similarity index 100% rename from ServerIntegrationTests/ServerIntegrationTests.vcxproj rename to tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj diff --git a/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters b/tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters similarity index 100% rename from ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters rename to tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters diff --git a/ServerUnitTest/ParseMessageUnitTests.cpp b/tests/ServerUnitTest/ParseMessageUnitTests.cpp similarity index 100% rename from ServerUnitTest/ParseMessageUnitTests.cpp rename to tests/ServerUnitTest/ParseMessageUnitTests.cpp diff --git a/ServerUnitTest/SerializeMessageUnitTests.cpp b/tests/ServerUnitTest/SerializeMessageUnitTests.cpp similarity index 100% rename from ServerUnitTest/SerializeMessageUnitTests.cpp rename to tests/ServerUnitTest/SerializeMessageUnitTests.cpp diff --git a/ServerUnitTest/ServerUnitTest.vcxproj b/tests/ServerUnitTest/ServerUnitTest.vcxproj similarity index 100% rename from ServerUnitTest/ServerUnitTest.vcxproj rename to tests/ServerUnitTest/ServerUnitTest.vcxproj diff --git a/ServerUnitTest/ServerUnitTest.vcxproj.filters b/tests/ServerUnitTest/ServerUnitTest.vcxproj.filters similarity index 100% rename from ServerUnitTest/ServerUnitTest.vcxproj.filters rename to tests/ServerUnitTest/ServerUnitTest.vcxproj.filters From e98e57fcd0773158eef3cd4aa19f91019cc24193 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 19:22:02 +0200 Subject: [PATCH 037/203] Create cmakefile and add shared --- CMakeLists.txt | 5 +++++ shared/CMakeLists.txt | 11 +++++++++++ 2 files changed, 16 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 shared/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..400d50b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.15) + +project(Pine) + +add_subdirectory(shared) \ No newline at end of file diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt new file mode 100644 index 0000000..0e3a212 --- /dev/null +++ b/shared/CMakeLists.txt @@ -0,0 +1,11 @@ +project(shared) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +file(GLOB HEADER_FILES "include/*.h") +file(GLOB SOURCE_FILES "src/*.cpp") + +add_library(shared STATIC ${HEADER_FILES} ${SOURCE_FILES}) + +target_include_directories(shared PUBLIC include) \ No newline at end of file From db58a578d0733df64d000ee5ee89f63f6a6c07d5 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 19:22:37 +0200 Subject: [PATCH 038/203] Move snowflake to shared and delete visual studio files --- Server/Snowflake.h | 83 ----------------- Shared/Shared.vcxproj | 147 ------------------------------ Shared/Shared.vcxproj.filters | 22 ----- shared/include/snowflake.h | 36 ++++++++ {Shared => shared/src}/shared.cpp | 0 shared/src/snowflake.cpp | 66 ++++++++++++++ 6 files changed, 102 insertions(+), 252 deletions(-) delete mode 100644 Server/Snowflake.h delete mode 100644 Shared/Shared.vcxproj delete mode 100644 Shared/Shared.vcxproj.filters create mode 100644 shared/include/snowflake.h rename {Shared => shared/src}/shared.cpp (100%) create mode 100644 shared/src/snowflake.cpp diff --git a/Server/Snowflake.h b/Server/Snowflake.h deleted file mode 100644 index 020da11..0000000 --- a/Server/Snowflake.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -struct Snowflake -{ - Snowflake() - { - using namespace std::chrono; - Value.timestamp = duration_cast(system_clock::now().time_since_epoch()).count(); - Value.workerId = 0; - Value.processId = 0; - if (lastMillisecond == Value.timestamp) - Value.sequence++; - else - Value.sequence = 0; - } - - template - requires std::is_integral_v - explicit Snowflake(T const& id) - { - Value.timestamp = id >> 22; - Value.workerId = (id >> 17) & 0x1F; - Value.processId = (id >> 12) & 0x1F; - Value.sequence = id & 0xFFF; - } - - struct - { - uint64_t timestamp : 42; - uint64_t workerId : 5; - uint64_t processId : 5; - uint64_t sequence : 12; - } Value; - - template - requires std::is_integral_v - Snowflake operator =(T const& id) - { - return Snowflake(id); - } - - bool operator !=(Snowflake const& other) const - { - return !(*this == other); - } - - bool operator ==(Snowflake const& other) const - { - return Value.timestamp == other.Value.timestamp - && Value.workerId == other.Value.workerId - && Value.processId == other.Value.processId - && Value.sequence == other.Value.sequence; - } - - template - requires std::is_integral_v - bool operator ==(T const& other) const - { - return Snowflake(other) == *this; - } - - template - requires std::is_integral_v - explicit(false) constexpr operator T() const - { - return static_cast(Value.timestamp) << 22 - | static_cast(Value.workerId) << 17 - | static_cast(Value.processId) << 12 - | static_cast(Value.sequence); - } - - friend std::ostream& operator<<(std::ostream& out, Snowflake const& snowflake) - { - return out << static_cast(snowflake); - } - - static inline uint64_t lastMillisecond = 0; -}; \ No newline at end of file diff --git a/Shared/Shared.vcxproj b/Shared/Shared.vcxproj deleted file mode 100644 index e67ceca..0000000 --- a/Shared/Shared.vcxproj +++ /dev/null @@ -1,147 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {ffa917e0-c449-48c7-bdf3-9877cbd96b46} - Shared - 10.0 - - - - StaticLibrary - true - v143 - Unicode - - - StaticLibrary - false - v143 - true - Unicode - - - StaticLibrary - true - v143 - Unicode - - - StaticLibrary - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpplatest - true - true - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpplatest - true - true - - - Console - true - true - true - - - - - Level3 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpplatest - true - true - - - Console - true - - - - - Level3 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpplatest - true - true - - - Console - true - true - true - - - - - - - - - \ No newline at end of file diff --git a/Shared/Shared.vcxproj.filters b/Shared/Shared.vcxproj.filters deleted file mode 100644 index 146c5a0..0000000 --- a/Shared/Shared.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - \ No newline at end of file diff --git a/shared/include/snowflake.h b/shared/include/snowflake.h new file mode 100644 index 0000000..73ba83b --- /dev/null +++ b/shared/include/snowflake.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +namespace pine +{ + struct snowflake + { + snowflake(); + + explicit snowflake(uint64_t const& id); + + struct + { + uint64_t timestamp : 42; + uint64_t worker_id : 5; + uint64_t process_id : 5; + uint64_t sequence : 12; + } value; + + snowflake operator=(uint64_t const& id); + + bool operator !=(snowflake const& other) const; + + bool operator ==(snowflake const& other) const; + + bool operator ==(uint64_t const& other) const; + + explicit(false) constexpr operator uint64_t() const; + + friend std::ostream& operator<<(std::ostream& out, snowflake const& snowflake); + + static uint64_t last_millisecond; + }; +} \ No newline at end of file diff --git a/Shared/shared.cpp b/shared/src/shared.cpp similarity index 100% rename from Shared/shared.cpp rename to shared/src/shared.cpp diff --git a/shared/src/snowflake.cpp b/shared/src/snowflake.cpp new file mode 100644 index 0000000..3a876ee --- /dev/null +++ b/shared/src/snowflake.cpp @@ -0,0 +1,66 @@ +#include "snowflake.h" + +#include +#include +#include + +namespace pine +{ + uint64_t snowflake::last_millisecond = 0; + + snowflake::snowflake() + { + using namespace std::chrono; + value.timestamp = duration_cast(system_clock::now().time_since_epoch()).count(); + value.worker_id = 0; + value.process_id = 0; + if (last_millisecond == value.timestamp) + value.sequence++; + else + value.sequence = 0; + } + + snowflake::snowflake(uint64_t const& id) + { + value.timestamp = id >> 22; + value.worker_id = (id >> 17) & 0x1F; + value.process_id = (id >> 12) & 0x1F; + value.sequence = id & 0xFFF; + } + + snowflake snowflake::operator=(uint64_t const& id) + { + return snowflake(id); + } + + bool snowflake::operator !=(snowflake const& other) const + { + return !(*this == other); + } + + bool snowflake::operator ==(snowflake const& other) const + { + return value.timestamp == other.value.timestamp + && value.worker_id == other.value.worker_id + && value.process_id == other.value.process_id + && value.sequence == other.value.sequence; + } + + bool snowflake::operator ==(uint64_t const& other) const + { + return static_cast(*this) == other; + } + + snowflake::operator uint64_t() const + { + return (value.timestamp << 22) + | (value.worker_id << 17) + | (value.process_id << 12) + | value.sequence; + } + + std::ostream& operator<<(std::ostream& out, snowflake const& snowflake) + { + return out << static_cast(snowflake); + } +} \ No newline at end of file From 61b2a18b8b3a23e1070701f9827d4b0c3e12e5b5 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 19:50:05 +0200 Subject: [PATCH 039/203] Fix return type to constexpr --- shared/src/snowflake.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/snowflake.cpp b/shared/src/snowflake.cpp index 3a876ee..d0f0832 100644 --- a/shared/src/snowflake.cpp +++ b/shared/src/snowflake.cpp @@ -51,7 +51,7 @@ namespace pine return static_cast(*this) == other; } - snowflake::operator uint64_t() const + constexpr snowflake::operator uint64_t() const { return (value.timestamp << 22) | (value.worker_id << 17) From b7259807d8167613b7d31e79f37a2ec5a823532c Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 20:22:23 +0200 Subject: [PATCH 040/203] Document --- shared/include/snowflake.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/shared/include/snowflake.h b/shared/include/snowflake.h index 73ba83b..fd9c25f 100644 --- a/shared/include/snowflake.h +++ b/shared/include/snowflake.h @@ -5,12 +5,24 @@ namespace pine { + /// + /// Represents a unique identifier for an object. + /// struct snowflake { + /// + /// Creates a new snowflake with a new generated id. + /// snowflake(); + /// + /// Creates a new snowflake from an id. + /// explicit snowflake(uint64_t const& id); + /// + /// The value of the snowflake. + /// struct { uint64_t timestamp : 42; @@ -19,6 +31,9 @@ namespace pine uint64_t sequence : 12; } value; + /// + /// Creates a new snowflake from an id. + /// snowflake operator=(uint64_t const& id); bool operator !=(snowflake const& other) const; @@ -29,8 +44,15 @@ namespace pine explicit(false) constexpr operator uint64_t() const; + /// + /// Writes the snowflake to the output stream. + /// friend std::ostream& operator<<(std::ostream& out, snowflake const& snowflake); + private: + /// + /// The last millisecond a snowflake was generated. + /// static uint64_t last_millisecond; }; } \ No newline at end of file From d5b2e4b35ae56eeb53103d4e97595239ed0c3b43 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 20:22:42 +0200 Subject: [PATCH 041/203] Add content table and snowflake specifications --- specs.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/specs.md b/specs.md index b5d0b99..84c9cbd 100644 --- a/specs.md +++ b/specs.md @@ -2,6 +2,16 @@ # Version 2 +## Content table + +- [How to establish a connection](#how-to-establish-a-connection) +- [Message format](#message-format) +- [Snowflake](#snowflake) +- [Keep alive process](#keep-alive-process) +- [Error handling](#error-handling) +- [Message types](#message-types) +- [Error codes](#error-codes) + ## How to establish a connection | Emitter | Description | @@ -25,9 +35,18 @@ |-------|-------------| | 1 | Message type | | 8 | Message Body length | -| 8 | Message Id | +| 8 | Message [Snowflake](#Snowflake) Id | | 0-XXXX | Message data | +## Snowflake + +A snowflake is a 64-bit integer that is used to identify a message. +It is generated by the server and sent to the client. + +| Bits | 64 - 22 | 21 - 10 | 9 - 0 | +| | ------- | ------- | ----- | +| Description | Timestamp | Worker Id | Machine sequence number | + ## Keep alive process Client should send a keep alive message every 5 seconds. From b7a1673d3a90ad094a35b224906459c34bec1b95 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 21:32:58 +0200 Subject: [PATCH 042/203] Include direct file to std::ostream --- shared/include/snowflake.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/include/snowflake.h b/shared/include/snowflake.h index fd9c25f..1ade087 100644 --- a/shared/include/snowflake.h +++ b/shared/include/snowflake.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace pine { From 5bd413ce5701b2cc6535750b52a39e8d70f1514f Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 21:33:12 +0200 Subject: [PATCH 043/203] Remove obsolete keywords --- shared/include/snowflake.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/include/snowflake.h b/shared/include/snowflake.h index 1ade087..a0c5d50 100644 --- a/shared/include/snowflake.h +++ b/shared/include/snowflake.h @@ -42,7 +42,7 @@ namespace pine bool operator ==(uint64_t const& other) const; - explicit(false) constexpr operator uint64_t() const; + constexpr operator uint64_t() const; /// /// Writes the snowflake to the output stream. From 1d56c25209255b929e3704ba0d4c8306956bec2e Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 21:33:45 +0200 Subject: [PATCH 044/203] Setup tests --- CMakeLists.txt | 7 ++- tests/CMakeLists.txt | 3 ++ tests/unit/CMakeLists.txt | 16 +++++++ tests/unit/snowflake_tests.cpp | 80 ++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/unit/CMakeLists.txt create mode 100644 tests/unit/snowflake_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 400d50b..7bb8cc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,4 +2,9 @@ cmake_minimum_required(VERSION 3.15) project(Pine) -add_subdirectory(shared) \ No newline at end of file +enable_testing() + +find_package(GTest CONFIG REQUIRED) + +add_subdirectory(shared) +add_subdirectory(tests) \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..6521e39 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,3 @@ +project(tests) + +add_subdirectory(unit) \ No newline at end of file diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt new file mode 100644 index 0000000..d0a1eb1 --- /dev/null +++ b/tests/unit/CMakeLists.txt @@ -0,0 +1,16 @@ +include_directories() + +add_executable( + unit_tests + snowflake_tests.cpp +) + +target_link_libraries(unit_tests PRIVATE shared) +target_link_libraries(unit_tests PRIVATE GTest::gtest GTest::gtest_main GTest::gmock GTest::gmock_main) + +add_test( + NAME + UnitTests + COMMAND + unit_tests +) \ No newline at end of file diff --git a/tests/unit/snowflake_tests.cpp b/tests/unit/snowflake_tests.cpp new file mode 100644 index 0000000..e8e4276 --- /dev/null +++ b/tests/unit/snowflake_tests.cpp @@ -0,0 +1,80 @@ +#include + +#include +#include +#include + +#include "snowflake.h" + +TEST(snowflake_test, default_constructor) +{ + pine::snowflake snowflake; + EXPECT_EQ(snowflake.value.worker_id, 0); + EXPECT_EQ(snowflake.value.process_id, 0); + EXPECT_EQ(snowflake.value.sequence, 0); +} + +TEST(snowflake_test, constructor) +{ + pine::snowflake snowflake(0x0000000000000001); + EXPECT_EQ(snowflake.value.timestamp, 0); + EXPECT_EQ(snowflake.value.worker_id, 0); + EXPECT_EQ(snowflake.value.process_id, 0); + EXPECT_EQ(snowflake.value.sequence, 1); +} + +TEST(snowflake_test, assignment_operator) +{ + pine::snowflake snowflake; + snowflake = 0x0000000000000001; + EXPECT_EQ(snowflake.value.timestamp, 0); + EXPECT_EQ(snowflake.value.worker_id, 0); + EXPECT_EQ(snowflake.value.process_id, 0); + EXPECT_EQ(snowflake.value.sequence, 1); +} + +TEST(snowflake_test, equality_operator) +{ + pine::snowflake snowflake1(0x0000000000000001); + pine::snowflake snowflake2(0x0000000000000001); + EXPECT_TRUE(snowflake1 == snowflake2); +} + +TEST(snowflake_test, inequality_operator) +{ + pine::snowflake snowflake1(0x0000000000000001); + pine::snowflake snowflake2(0x0000000000000002); + EXPECT_TRUE(snowflake1 != snowflake2); +} + +TEST(snowflake_test, equality_operator_with_uint64_t) +{ + pine::snowflake snowflake(0x0000000000000001); + EXPECT_TRUE(snowflake == (uint64_t)0x0000000000000001); +} + +TEST(snowflake_test, inequality_operator_with_uint64_t) +{ + pine::snowflake snowflake(0x0000000000000001); + EXPECT_TRUE(snowflake != (uint64_t)0x0000000000000002); +} + +TEST(snowflake_test, cast_to_uint64_t) +{ + pine::snowflake snowflake(0x0000000000000001); + EXPECT_EQ((uint64_t)snowflake, 0x0000000000000001); +} + +TEST(snowflake_test, stream_operator) +{ + pine::snowflake snowflake(0x0000000000000001); + std::stringstream stream; + stream << snowflake; + EXPECT_EQ(stream.str(), "1"); +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file From 59e990305d6ee8476b6ce00f7572a7b4eee70de8 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 21:34:10 +0200 Subject: [PATCH 045/203] Fix snowflake assigment operator --- shared/src/snowflake.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shared/src/snowflake.cpp b/shared/src/snowflake.cpp index d0f0832..ae390cf 100644 --- a/shared/src/snowflake.cpp +++ b/shared/src/snowflake.cpp @@ -30,7 +30,9 @@ namespace pine snowflake snowflake::operator=(uint64_t const& id) { - return snowflake(id); + *this = snowflake(id); + + return *this; } bool snowflake::operator !=(snowflake const& other) const From 661b23707865046dee453b98226ba32c17dc6029 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 22:46:19 +0200 Subject: [PATCH 046/203] Move message to cmake project --- Server/Message.cpp | 89 ------------------------------ Server/Message.h | 53 ------------------ shared/include/message.h | 116 +++++++++++++++++++++++++++++++++++++++ shared/src/message.cpp | 59 ++++++++++++++++++++ 4 files changed, 175 insertions(+), 142 deletions(-) delete mode 100644 Server/Message.cpp delete mode 100644 Server/Message.h create mode 100644 shared/include/message.h create mode 100644 shared/src/message.cpp diff --git a/Server/Message.cpp b/Server/Message.cpp deleted file mode 100644 index ecd3871..0000000 --- a/Server/Message.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include -#include - -#include "Message.h" -#include "HelloMessage.h" - -namespace SocketMessages -{ - MessageHeader::MessageHeader(std::vector const& buffer) - { - Parse(buffer); - } - - void MessageHeader::Parse(std::vector const& buffer) - { - std::memcpy(&bodySize, &buffer[1], sizeof(bodySize)); - - switch (uint8_t messageType_ = buffer[0]) - { - using enum MessageType; - - case static_cast(AcknowledgeMessage): - messageType = AcknowledgeMessage; - break; - - case static_cast(HelloMessage): - messageType = HelloMessage; - break; - - case static_cast(IdentifyMessage): - messageType = IdentifyMessage; - break; - - case static_cast(KeepAliveMessage): - messageType = KeepAliveMessage; - break; - - case static_cast(SendChatMessage): - messageType = SendChatMessage; - break; - - case static_cast(ReceiveChatMessage): - messageType = ReceiveChatMessage; - break; - - case static_cast(ErrorMessage): - messageType = ErrorMessage; - break; - - default: - messageType = InvalidMessage; - break; - } - - uint64_t id; - std::memcpy(&id, &buffer[sizeof(messageType) + sizeof(bodySize)], sizeof(id)); - messageId = Snowflake{ id }; - } - - std::vector MessageHeader::Serialize() const - { - std::vector buffer; - buffer.push_back(static_cast(messageType)); - buffer.resize(size); - // copy body size - std::memcpy(&buffer[sizeof(messageType)], &bodySize, sizeof(bodySize)); - // copy message id - uint64_t id = messageId; - std::memcpy(&buffer[sizeof(messageType) + sizeof(bodySize)], &id, sizeof(id)); - return buffer; - } - - bool Message::ParseBody(std::vector const& buffer) - { - return false; - } - - std::vector Message::Serialize() const - { - std::vector buffer(MessageHeader::size, 0); - return buffer; - } - - uint64_t Message::GetBodySize() const - { - return 0; - } -} \ No newline at end of file diff --git a/Server/Message.h b/Server/Message.h deleted file mode 100644 index 0fb4851..0000000 --- a/Server/Message.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "Snowflake.h" - -constexpr uint64_t ChatMessageMinLength = 0x01; -constexpr uint64_t ChatMessageMaxLength = 2000; -constexpr uint64_t CurrentVersion = 0x2; -constexpr uint64_t UsernameMinLength = 0x03; -constexpr uint64_t UsernameMaxLength = 0x20; - -namespace SocketMessages -{ - enum class MessageType : uint8_t - { - InvalidMessage, - AcknowledgeMessage, - HelloMessage, - IdentifyMessage, - KeepAliveMessage, - SendChatMessage, - ReceiveChatMessage, - ErrorMessage, - }; - - struct MessageHeader - { - MessageHeader() = default; - explicit MessageHeader(std::vector const& buffer); - void Parse(std::vector const& buffer); - std::vector Serialize() const; - - - MessageType messageType{}; - uint64_t bodySize{}; - Snowflake messageId{}; - - static uint64_t constexpr size = sizeof(messageType) + sizeof(uint64_t) + sizeof(messageId.Value); - }; - - struct Message : std::enable_shared_from_this - { - virtual ~Message() = default; - virtual uint64_t GetBodySize() const; - virtual bool ParseBody(std::vector const& buffer); - virtual std::vector Serialize() const; - - MessageHeader header{}; - }; -} \ No newline at end of file diff --git a/shared/include/message.h b/shared/include/message.h new file mode 100644 index 0000000..e2b9b2c --- /dev/null +++ b/shared/include/message.h @@ -0,0 +1,116 @@ +#pragma once + +#include +#include +#include + +#include "snowflake.h" + +namespace pine +{ + /// + /// Minimum length of a chat message. It is used to prevent spam. Equals to 1. + /// + constexpr auto chat_message_min_length = 0x01; + + /// + /// Maximum length of a chat message. It is used to prevent spam. Equals to 2000. + /// + constexpr auto chat_message_max_length = 2000; + + /// + /// Current version of the protocol. + /// + constexpr auto current_version = 0x2; + + /// + /// Minimum length of a username. Equals to 3. + /// + constexpr auto username_min_length = 0x03; + + /// + /// Maximum length of a username. Equals to 32. + /// + constexpr auto username_max_length = 0x20; + + namespace socket_messages + { + enum message_type : uint8_t + { + invalid_message, + acknowledge_message, + hello_message, + identify_message, + keep_alive_message, + send_chat_message, + receive_chat_message, + error_message, + }; + + /// + /// Represents the header of a message. It contains the type of the message, the size of the body and the id of the message. + /// It is located at the beginning of every message. + /// + struct message_header + { + message_header() = default; + + /// + /// Creates a new message header from a buffer. The buffer must contain at least 13 bytes. + /// The first byte is the type of the message, the next 8 bytes are the size of the body and the last 8 bytes are the id of the message. + /// + /// + explicit message_header(std::vector const& buffer); + + /// + /// Loads a message from a buffer. The buffer must contain at least 13 bytes. + /// The first byte is the type of the message, the next 8 bytes are the size of the body and the last 8 bytes are the id of the message. + /// + void parse(std::vector const& buffer); + + /// + /// Serializes the message header to a buffer. + /// + std::vector serialize() const; + + + message_type type_of_message{}; + uint64_t body_size{}; + snowflake message_id{}; + + /// + /// The size of the message header in bytes. + /// + static uint64_t constexpr size = sizeof(message_type) + sizeof(uint64_t) + sizeof(message_id.value); + }; + + /// + /// Represents a message. It contains a header and a body. + /// + struct message : std::enable_shared_from_this + { + virtual ~message() = default; + + /// + /// Returns the number of bytes of the body. + /// + virtual uint64_t get_body_size() const; + + /// + /// Loads the body of the message from a buffer. + /// + /// True if the body was loaded successfully, false otherwise. + virtual bool parse_body(std::vector const& buffer); + + /// + /// Serializes the body of the message to a buffer. + /// + virtual std::vector serialize() const; + + /// + /// Returns the header of the message. + /// + message_header header{}; + }; + } +} diff --git a/shared/src/message.cpp b/shared/src/message.cpp new file mode 100644 index 0000000..5a66759 --- /dev/null +++ b/shared/src/message.cpp @@ -0,0 +1,59 @@ +#include +#include + +#include "message.h" +#include "snowflake.h" + +namespace pine::socket_messages +{ + message_header::message_header(std::vector const& buffer) + { + parse(buffer); + } + + void message_header::parse(std::vector const& buffer) + { + std::memcpy(&body_size, &buffer[1], sizeof(body_size)); + + type_of_message = static_cast(buffer[0]); + + + uint64_t id; + std::memcpy(&id, &buffer[sizeof(type_of_message) + sizeof(body_size)], sizeof(id)); + message_id = snowflake{ id }; + } + + std::vector message_header::serialize() const + { + std::vector buffer; + + buffer.push_back(static_cast(type_of_message)); + buffer.resize(size); + + // copy body size + std::memcpy(&buffer[sizeof(type_of_message)], &body_size, sizeof(body_size)); + + // copy message id + uint64_t id = message_id; + + std::memcpy(&buffer[sizeof(type_of_message) + sizeof(body_size)], &id, sizeof(id)); + + return buffer; + } + + bool message::parse_body(std::vector const& buffer) + { + return false; + } + + std::vector message::serialize() const + { + std::vector buffer(message_header::size, 0); + return buffer; + } + + uint64_t message::get_body_size() const + { + return 0; + } +} \ No newline at end of file From 013ab2dc8d2b5ec601aed2e491c030a83bc6e665 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 22:56:55 +0200 Subject: [PATCH 047/203] Conform to Github's markdown standards --- specs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs.md b/specs.md index 84c9cbd..5c29c8a 100644 --- a/specs.md +++ b/specs.md @@ -44,7 +44,7 @@ A snowflake is a 64-bit integer that is used to identify a message. It is generated by the server and sent to the client. | Bits | 64 - 22 | 21 - 10 | 9 - 0 | -| | ------- | ------- | ----- | +| --- | ------- | ------- | ----- | | Description | Timestamp | Worker Id | Machine sequence number | ## Keep alive process From 1b87cf4252f4e9a3fa93eb85f5d1b8375ebaea2b Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 23:10:27 +0200 Subject: [PATCH 048/203] Use simpler names for members --- shared/include/message.h | 6 +++--- shared/src/message.cpp | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/shared/include/message.h b/shared/include/message.h index e2b9b2c..796f0db 100644 --- a/shared/include/message.h +++ b/shared/include/message.h @@ -74,14 +74,14 @@ namespace pine std::vector serialize() const; - message_type type_of_message{}; + message_type type{}; uint64_t body_size{}; - snowflake message_id{}; + snowflake id{}; /// /// The size of the message header in bytes. /// - static uint64_t constexpr size = sizeof(message_type) + sizeof(uint64_t) + sizeof(message_id.value); + static uint64_t constexpr size = sizeof(message_type) + sizeof(uint64_t) + sizeof(id.value); }; /// diff --git a/shared/src/message.cpp b/shared/src/message.cpp index 5a66759..bfb726e 100644 --- a/shared/src/message.cpp +++ b/shared/src/message.cpp @@ -15,28 +15,28 @@ namespace pine::socket_messages { std::memcpy(&body_size, &buffer[1], sizeof(body_size)); - type_of_message = static_cast(buffer[0]); + type = static_cast(buffer[0]); - uint64_t id; - std::memcpy(&id, &buffer[sizeof(type_of_message) + sizeof(body_size)], sizeof(id)); - message_id = snowflake{ id }; + uint64_t new_id = 0; + std::memcpy(&id, &buffer[sizeof(type) + sizeof(body_size)], sizeof(new_id)); + id = snowflake{ new_id }; } std::vector message_header::serialize() const { std::vector buffer; - buffer.push_back(static_cast(type_of_message)); + buffer.push_back(static_cast(type)); buffer.resize(size); // copy body size - std::memcpy(&buffer[sizeof(type_of_message)], &body_size, sizeof(body_size)); + std::memcpy(&buffer[sizeof(type)], &body_size, sizeof(body_size)); // copy message id - uint64_t id = message_id; + uint64_t new_id = id; - std::memcpy(&buffer[sizeof(type_of_message) + sizeof(body_size)], &id, sizeof(id)); + std::memcpy(&buffer[sizeof(type) + sizeof(body_size)], &new_id, sizeof(new_id)); return buffer; } From c82ae031d64832fe6868f987ba6f66c31097ff2a Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 23:10:48 +0200 Subject: [PATCH 049/203] Rename snowflake_test to snowflake --- tests/unit/snowflake_tests.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/unit/snowflake_tests.cpp b/tests/unit/snowflake_tests.cpp index e8e4276..a6014c5 100644 --- a/tests/unit/snowflake_tests.cpp +++ b/tests/unit/snowflake_tests.cpp @@ -6,7 +6,7 @@ #include "snowflake.h" -TEST(snowflake_test, default_constructor) +TEST(snowflake, default_constructor) { pine::snowflake snowflake; EXPECT_EQ(snowflake.value.worker_id, 0); @@ -14,7 +14,7 @@ TEST(snowflake_test, default_constructor) EXPECT_EQ(snowflake.value.sequence, 0); } -TEST(snowflake_test, constructor) +TEST(snowflake, constructor) { pine::snowflake snowflake(0x0000000000000001); EXPECT_EQ(snowflake.value.timestamp, 0); @@ -23,7 +23,7 @@ TEST(snowflake_test, constructor) EXPECT_EQ(snowflake.value.sequence, 1); } -TEST(snowflake_test, assignment_operator) +TEST(snowflake, assignment_operator) { pine::snowflake snowflake; snowflake = 0x0000000000000001; @@ -33,39 +33,39 @@ TEST(snowflake_test, assignment_operator) EXPECT_EQ(snowflake.value.sequence, 1); } -TEST(snowflake_test, equality_operator) +TEST(snowflake, equality_operator) { pine::snowflake snowflake1(0x0000000000000001); pine::snowflake snowflake2(0x0000000000000001); EXPECT_TRUE(snowflake1 == snowflake2); } -TEST(snowflake_test, inequality_operator) +TEST(snowflake, inequality_operator) { pine::snowflake snowflake1(0x0000000000000001); pine::snowflake snowflake2(0x0000000000000002); EXPECT_TRUE(snowflake1 != snowflake2); } -TEST(snowflake_test, equality_operator_with_uint64_t) +TEST(snowflake, equality_operator_with_uint64_t) { pine::snowflake snowflake(0x0000000000000001); EXPECT_TRUE(snowflake == (uint64_t)0x0000000000000001); } -TEST(snowflake_test, inequality_operator_with_uint64_t) +TEST(snowflake, inequality_operator_with_uint64_t) { pine::snowflake snowflake(0x0000000000000001); EXPECT_TRUE(snowflake != (uint64_t)0x0000000000000002); } -TEST(snowflake_test, cast_to_uint64_t) +TEST(snowflake, cast_to_uint64_t) { pine::snowflake snowflake(0x0000000000000001); EXPECT_EQ((uint64_t)snowflake, 0x0000000000000001); } -TEST(snowflake_test, stream_operator) +TEST(snowflake, stream_operator) { pine::snowflake snowflake(0x0000000000000001); std::stringstream stream; From 917dfd6383cb5d9f5dd3d42f8a784d5db2603951 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 23:10:59 +0200 Subject: [PATCH 050/203] Write message_header tests --- tests/unit/CMakeLists.txt | 4 +-- tests/unit/message_tests.cpp | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 tests/unit/message_tests.cpp diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index d0a1eb1..4ddc584 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -2,8 +2,8 @@ include_directories() add_executable( unit_tests - snowflake_tests.cpp -) + "snowflake_tests.cpp" + "message_tests.cpp") target_link_libraries(unit_tests PRIVATE shared) target_link_libraries(unit_tests PRIVATE GTest::gtest GTest::gtest_main GTest::gmock GTest::gmock_main) diff --git a/tests/unit/message_tests.cpp b/tests/unit/message_tests.cpp new file mode 100644 index 0000000..49eb6be --- /dev/null +++ b/tests/unit/message_tests.cpp @@ -0,0 +1,57 @@ +#include + +#include "message.h" + +TEST(message_header, default_constructor) +{ + pine::socket_messages::message_header header; + EXPECT_EQ(header.type, pine::socket_messages::message_type::invalid_message); + EXPECT_EQ(header.body_size, 0); + EXPECT_NE(header.id, 0ull); +} + +TEST(message_header, construct_from_buffer) +{ + std::vector buffer{ + 1, // type + 0, 0, 0, 0, 0, 0, 0, 8, // body size + 1, 2, 3, 4, 5, 6, 7, 8, // id + }; + + pine::socket_messages::message_header header(buffer); + + EXPECT_EQ(header.type, pine::socket_messages::message_type::hello_message); + EXPECT_EQ(header.body_size, 8); + EXPECT_EQ(header.id, 0x0102030405060708ull); +} + +TEST(message_header, parse) +{ + std::vector buffer{ + 1, // type + 0, 0, 0, 0, 0, 0, 0, 8, // body size + 1, 2, 3, 4, 5, 6, 7, 8, // id + }; + + pine::socket_messages::message_header header; + header.parse(buffer); + + EXPECT_EQ(header.type, pine::socket_messages::message_type::hello_message); + EXPECT_EQ(header.body_size, 8); + EXPECT_EQ(header.id, 0x0102030405060708ull); +} + +TEST(message_header, serialize) +{ + pine::socket_messages::message_header header; + + header.type = pine::socket_messages::message_type::hello_message; + header.body_size = 8; + header.id = 0x0102030405060708ull; + + auto buffer = header.serialize(); + + EXPECT_EQ(buffer[0], 1); + EXPECT_EQ(*(uint64_t*)(&buffer[1]), 8); + EXPECT_EQ(*(uint64_t*)(&buffer[9]), 0x0102030405060708ull); +} \ No newline at end of file From b2e25901053f3e0bf24893f8e7c8d1e1fa959e86 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 23:29:32 +0200 Subject: [PATCH 051/203] Fix test using wrong endian numbers --- shared/src/message.cpp | 5 ++--- tests/unit/message_tests.cpp | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/shared/src/message.cpp b/shared/src/message.cpp index bfb726e..4c102a3 100644 --- a/shared/src/message.cpp +++ b/shared/src/message.cpp @@ -17,10 +17,9 @@ namespace pine::socket_messages type = static_cast(buffer[0]); - uint64_t new_id = 0; - std::memcpy(&id, &buffer[sizeof(type) + sizeof(body_size)], sizeof(new_id)); - id = snowflake{ new_id }; + std::memcpy(&new_id, &buffer[sizeof(type) + sizeof(body_size)], sizeof(new_id)); + id = new_id; } std::vector message_header::serialize() const diff --git a/tests/unit/message_tests.cpp b/tests/unit/message_tests.cpp index 49eb6be..fd15f4f 100644 --- a/tests/unit/message_tests.cpp +++ b/tests/unit/message_tests.cpp @@ -13,32 +13,32 @@ TEST(message_header, default_constructor) TEST(message_header, construct_from_buffer) { std::vector buffer{ - 1, // type - 0, 0, 0, 0, 0, 0, 0, 8, // body size - 1, 2, 3, 4, 5, 6, 7, 8, // id + pine::socket_messages::message_type::hello_message, // type + 1, 0, 0, 0, 0, 0, 0, 0, // body size + 1, 0, 0, 0, 0, 0, 0, 0, // id }; pine::socket_messages::message_header header(buffer); EXPECT_EQ(header.type, pine::socket_messages::message_type::hello_message); - EXPECT_EQ(header.body_size, 8); - EXPECT_EQ(header.id, 0x0102030405060708ull); + EXPECT_EQ(header.body_size, 1); + EXPECT_EQ(header.id, 1ull); } TEST(message_header, parse) { std::vector buffer{ - 1, // type - 0, 0, 0, 0, 0, 0, 0, 8, // body size - 1, 2, 3, 4, 5, 6, 7, 8, // id + pine::socket_messages::message_type::hello_message, // type + 1, 0, 0, 0, 0, 0, 0, 0, // body size + 1, 0, 0, 0, 0, 0, 0, 0, // id }; pine::socket_messages::message_header header; header.parse(buffer); EXPECT_EQ(header.type, pine::socket_messages::message_type::hello_message); - EXPECT_EQ(header.body_size, 8); - EXPECT_EQ(header.id, 0x0102030405060708ull); + EXPECT_EQ(header.body_size, 1); + EXPECT_EQ(header.id, 1ull); } TEST(message_header, serialize) @@ -46,12 +46,12 @@ TEST(message_header, serialize) pine::socket_messages::message_header header; header.type = pine::socket_messages::message_type::hello_message; - header.body_size = 8; - header.id = 0x0102030405060708ull; + header.body_size = 1; + header.id = 1ull; auto buffer = header.serialize(); - EXPECT_EQ(buffer[0], 1); - EXPECT_EQ(*(uint64_t*)(&buffer[1]), 8); - EXPECT_EQ(*(uint64_t*)(&buffer[9]), 0x0102030405060708ull); + EXPECT_EQ(buffer[0], pine::socket_messages::message_type::hello_message); + EXPECT_EQ(*(uint64_t*)(&buffer[1]), 1); + EXPECT_EQ(*(uint64_t*)(&buffer[9]), 1ull); } \ No newline at end of file From df696ba044a52bacb3439950286965fc986aafca Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 23:32:50 +0200 Subject: [PATCH 052/203] Write tests for message --- tests/unit/message_tests.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/unit/message_tests.cpp b/tests/unit/message_tests.cpp index fd15f4f..c12dc06 100644 --- a/tests/unit/message_tests.cpp +++ b/tests/unit/message_tests.cpp @@ -54,4 +54,31 @@ TEST(message_header, serialize) EXPECT_EQ(buffer[0], pine::socket_messages::message_type::hello_message); EXPECT_EQ(*(uint64_t*)(&buffer[1]), 1); EXPECT_EQ(*(uint64_t*)(&buffer[9]), 1ull); -} \ No newline at end of file +} + +TEST(message, default_constructor) +{ + pine::socket_messages::message msg; + EXPECT_EQ(msg.header.type, pine::socket_messages::message_type::invalid_message); + EXPECT_EQ(msg.header.body_size, 0); + EXPECT_NE(msg.header.id, 0ull); +} + +TEST(message, get_body_size) +{ + pine::socket_messages::message msg; + EXPECT_EQ(msg.get_body_size(), 0); +} + +TEST(message, parse_body) +{ + pine::socket_messages::message msg; + EXPECT_FALSE(msg.parse_body({})); +} + +TEST(message, serialize) +{ + pine::socket_messages::message msg; + auto buffer = msg.serialize(); + EXPECT_EQ(buffer.size(), pine::socket_messages::message_header::size); +} From e9f195edf56a4f2b791d27a68f98558c2a980b88 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 23:37:54 +0200 Subject: [PATCH 053/203] Include vector --- tests/unit/message_tests.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit/message_tests.cpp b/tests/unit/message_tests.cpp index c12dc06..bc6a8d6 100644 --- a/tests/unit/message_tests.cpp +++ b/tests/unit/message_tests.cpp @@ -1,5 +1,7 @@ #include +#include + #include "message.h" TEST(message_header, default_constructor) From dc52129b968831b5ce0351788a44b4e384effeb6 Mon Sep 17 00:00:00 2001 From: Jacques Date: Thu, 7 Sep 2023 23:38:10 +0200 Subject: [PATCH 054/203] Move user to cmake project and remove getter/setter --- Server/User.cpp | 3 --- Server/User.h | 17 ----------------- shared/include/user.h | 16 ++++++++++++++++ shared/src/user.cpp | 7 +++++++ 4 files changed, 23 insertions(+), 20 deletions(-) delete mode 100644 Server/User.cpp delete mode 100644 Server/User.h create mode 100644 shared/include/user.h create mode 100644 shared/src/user.cpp diff --git a/Server/User.cpp b/Server/User.cpp deleted file mode 100644 index 306b307..0000000 --- a/Server/User.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "User.h" - -#include "Connection.h" \ No newline at end of file diff --git a/Server/User.h b/Server/User.h deleted file mode 100644 index 3444f62..0000000 --- a/Server/User.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -class User : public std::enable_shared_from_this -{ -public: - User() = default; - ~User() = default; - - [[nodiscard]] constexpr std::string const& GetUsername() const noexcept { return m_username; } - constexpr void SetUsername(std::string_view const& username) { m_username = username; } - -private: - std::string m_username{ "" }; -}; diff --git a/shared/include/user.h b/shared/include/user.h new file mode 100644 index 0000000..491ba48 --- /dev/null +++ b/shared/include/user.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace pine +{ + class user : public std::enable_shared_from_this + { + public: + user(std::string username) = default; + ~user() = default; + + const std::string username{ "" }; + }; +} diff --git a/shared/src/user.cpp b/shared/src/user.cpp new file mode 100644 index 0000000..c0aa564 --- /dev/null +++ b/shared/src/user.cpp @@ -0,0 +1,7 @@ +#include "user.h" + +namespace pine +{ + user::user(std::string username) : username(username) + {} +} \ No newline at end of file From b7880028fad158a0733f7c1722e2bdb117e4ab92 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 00:08:52 +0200 Subject: [PATCH 055/203] Move coroutine to cmake project --- Server/Coroutine.cpp | 1 - Server/Coroutine.h => shared/include/coroutine.h | 0 shared/src/Coroutine.cpp | 1 + 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 Server/Coroutine.cpp rename Server/Coroutine.h => shared/include/coroutine.h (100%) create mode 100644 shared/src/Coroutine.cpp diff --git a/Server/Coroutine.cpp b/Server/Coroutine.cpp deleted file mode 100644 index 51ccca4..0000000 --- a/Server/Coroutine.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "Coroutine.h" \ No newline at end of file diff --git a/Server/Coroutine.h b/shared/include/coroutine.h similarity index 100% rename from Server/Coroutine.h rename to shared/include/coroutine.h diff --git a/shared/src/Coroutine.cpp b/shared/src/Coroutine.cpp new file mode 100644 index 0000000..c362647 --- /dev/null +++ b/shared/src/Coroutine.cpp @@ -0,0 +1 @@ +#include "coroutine.h" \ No newline at end of file From 0ad328321fb9dc5caea20554819d44780acb720e Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 00:13:21 +0200 Subject: [PATCH 056/203] Move socket messages to cmake project --- shared/CMakeLists.txt | 4 ++-- Server/SocketMessages.h => shared/include/socket_messages.h | 0 .../include/socket_messages/ack_message.h | 0 .../include/socket_messages/error_message.h | 0 .../include/socket_messages/hello_message.h | 0 .../include/socket_messages/identify_message.h | 0 .../include/socket_messages/keep_alive_message.h | 0 .../include/socket_messages/receive_chat_message.h | 0 .../include/socket_messages/send_chat_message.h | 0 9 files changed, 2 insertions(+), 2 deletions(-) rename Server/SocketMessages.h => shared/include/socket_messages.h (100%) rename Server/ACKMessage.h => shared/include/socket_messages/ack_message.h (100%) rename Server/ErrorMessage.h => shared/include/socket_messages/error_message.h (100%) rename Server/HelloMessage.h => shared/include/socket_messages/hello_message.h (100%) rename Server/IdentifyMessage.h => shared/include/socket_messages/identify_message.h (100%) rename Server/KeepAliveMessage.h => shared/include/socket_messages/keep_alive_message.h (100%) rename Server/ReceiveChatMessage.h => shared/include/socket_messages/receive_chat_message.h (100%) rename Server/SendChatMessage.h => shared/include/socket_messages/send_chat_message.h (100%) diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 0e3a212..3cdb0b7 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -3,8 +3,8 @@ project(shared) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -file(GLOB HEADER_FILES "include/*.h") -file(GLOB SOURCE_FILES "src/*.cpp") +file(GLOB_RECURSE HEADER_FILES "include/*.h") +file(GLOB_RECURSE SOURCE_FILES "src/*.cpp") add_library(shared STATIC ${HEADER_FILES} ${SOURCE_FILES}) diff --git a/Server/SocketMessages.h b/shared/include/socket_messages.h similarity index 100% rename from Server/SocketMessages.h rename to shared/include/socket_messages.h diff --git a/Server/ACKMessage.h b/shared/include/socket_messages/ack_message.h similarity index 100% rename from Server/ACKMessage.h rename to shared/include/socket_messages/ack_message.h diff --git a/Server/ErrorMessage.h b/shared/include/socket_messages/error_message.h similarity index 100% rename from Server/ErrorMessage.h rename to shared/include/socket_messages/error_message.h diff --git a/Server/HelloMessage.h b/shared/include/socket_messages/hello_message.h similarity index 100% rename from Server/HelloMessage.h rename to shared/include/socket_messages/hello_message.h diff --git a/Server/IdentifyMessage.h b/shared/include/socket_messages/identify_message.h similarity index 100% rename from Server/IdentifyMessage.h rename to shared/include/socket_messages/identify_message.h diff --git a/Server/KeepAliveMessage.h b/shared/include/socket_messages/keep_alive_message.h similarity index 100% rename from Server/KeepAliveMessage.h rename to shared/include/socket_messages/keep_alive_message.h diff --git a/Server/ReceiveChatMessage.h b/shared/include/socket_messages/receive_chat_message.h similarity index 100% rename from Server/ReceiveChatMessage.h rename to shared/include/socket_messages/receive_chat_message.h diff --git a/Server/SendChatMessage.h b/shared/include/socket_messages/send_chat_message.h similarity index 100% rename from Server/SendChatMessage.h rename to shared/include/socket_messages/send_chat_message.h From 9025f019659574c409f4c84123e62c57531e5f2b Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 00:29:10 +0200 Subject: [PATCH 057/203] Implement ack_message in .cpp --- shared/include/socket_messages/ack_message.h | 66 +++++--------------- shared/src/socket_messages/ack_message.cpp | 51 +++++++++++++++ 2 files changed, 67 insertions(+), 50 deletions(-) create mode 100644 shared/src/socket_messages/ack_message.cpp diff --git a/shared/include/socket_messages/ack_message.h b/shared/include/socket_messages/ack_message.h index 5f6e4e1..c1d6651 100644 --- a/shared/include/socket_messages/ack_message.h +++ b/shared/include/socket_messages/ack_message.h @@ -1,59 +1,25 @@ #pragma once -#include #include #include -#include "Message.h" -#include "Snowflake.h" +#include "message.h" +#include "snowflake.h" -namespace SocketMessages +namespace pine::socket_messages { - struct AcknowledgeMessage : Message + struct acknowledge_message : message { - AcknowledgeMessage() - { - header.messageType = MessageType::AcknowledgeMessage; - header.bodySize = GetBodySize(); - } - - explicit AcknowledgeMessage(Snowflake const& id) - : m_messageId{ id } - { - header.messageType = MessageType::AcknowledgeMessage; - header.bodySize = GetBodySize(); - } - - bool ParseBody(std::vector const& buffer) override - { - if (buffer.size() != GetBodySize()) - return false; - - uint64_t id{}; - std::memcpy(std::bit_cast(&id), std::bit_cast(buffer.data()), sizeof(id)); - m_messageId = Snowflake(id); - - return true; - } - - std::vector Serialize() const override - { - std::vector buffer(MessageHeader::size + GetBodySize(), 0); - std::vector headerBuffer = header.Serialize(); - - std::memcpy(&buffer[0], &headerBuffer[0], MessageHeader::size); - auto id = static_cast(m_messageId); - std::memcpy(&buffer[MessageHeader::size], &id, sizeof(id)); - - return buffer; - } - - uint64_t GetBodySize() const final { return sizeof(Snowflake::Value); } - - [[nodiscard]] constexpr Snowflake const& GetAcknowledgedMessageId() const { return m_messageId; } - void SetAcknowledgedMessageId(Snowflake const& acknowledgedMessageId) { m_messageId = acknowledgedMessageId; } - - private: - Snowflake m_messageId{}; + acknowledge_message(); + + explicit acknowledge_message(snowflake const& id); + + bool parse_body(std::vector const& buffer) override; + + [[nodiscard]] std::vector serialize() const override; + + [[nodiscard]] uint64_t get_body_size() const final; + + snowflake id{}; }; -} \ No newline at end of file +} diff --git a/shared/src/socket_messages/ack_message.cpp b/shared/src/socket_messages/ack_message.cpp new file mode 100644 index 0000000..532d1cd --- /dev/null +++ b/shared/src/socket_messages/ack_message.cpp @@ -0,0 +1,51 @@ +#include "socket_messages/ack_message.h" + +#include +#include + +#include "message.h" + +namespace pine::socket_messages +{ + acknowledge_message::acknowledge_message() + { + header.type = message_type::acknowledge_message; + header.body_size = get_body_size(); + } + + acknowledge_message::acknowledge_message(snowflake const& id_) + : id{ id_ } + { + header.type = message_type::acknowledge_message; + header.body_size = get_body_size(); + } + + bool acknowledge_message::parse_body(std::vector const& buffer) + { + if (buffer.size() != get_body_size()) + return false; + + uint64_t new_id{}; + std::memcpy(std::bit_cast(&new_id), std::bit_cast(buffer.data()), sizeof(new_id)); + id = snowflake(new_id); + + return true; + } + + std::vector acknowledge_message::serialize() const + { + std::vector buffer(message_header::size + get_body_size(), 0); + std::vector header_buffer = header.serialize(); + + std::memcpy(&buffer[0], &header_buffer[0], message_header::size); + auto id_ = static_cast(id); + std::memcpy(&buffer[message_header::size], &id_, sizeof(id_)); + + return buffer; + } + + uint64_t acknowledge_message::get_body_size() const + { + return sizeof(snowflake::value); + } +} From 04319a50c3f7d6b99e0bc1eeb261d90b9d8b504f Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 00:39:27 +0200 Subject: [PATCH 058/203] Remove default constructor --- shared/include/user.h | 2 +- shared/src/user.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/shared/include/user.h b/shared/include/user.h index 491ba48..a3a072d 100644 --- a/shared/include/user.h +++ b/shared/include/user.h @@ -8,7 +8,7 @@ namespace pine class user : public std::enable_shared_from_this { public: - user(std::string username) = default; + user(std::string username); ~user() = default; const std::string username{ "" }; diff --git a/shared/src/user.cpp b/shared/src/user.cpp index c0aa564..d0f347c 100644 --- a/shared/src/user.cpp +++ b/shared/src/user.cpp @@ -1,5 +1,7 @@ #include "user.h" +#include + namespace pine { user::user(std::string username) : username(username) From f9f76883d522e6f76227773cf1ef0b4579327045 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 00:39:40 +0200 Subject: [PATCH 059/203] Enable C++20 --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bb8cc3..e8298d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,9 @@ cmake_minimum_required(VERSION 3.15) project(Pine) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + enable_testing() find_package(GTest CONFIG REQUIRED) From 7179619b25ff5e0c2267063672bf49f53dd0e0dd Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 00:40:47 +0200 Subject: [PATCH 060/203] Uppercase enum --- shared/include/message.h | 16 ++++++++-------- tests/unit/message_tests.cpp | 18 ++++++++++-------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/shared/include/message.h b/shared/include/message.h index 796f0db..d5c7656 100644 --- a/shared/include/message.h +++ b/shared/include/message.h @@ -37,14 +37,14 @@ namespace pine { enum message_type : uint8_t { - invalid_message, - acknowledge_message, - hello_message, - identify_message, - keep_alive_message, - send_chat_message, - receive_chat_message, - error_message, + INVALID_MESSAGE, + ACKNOWLEDGE_MESSAGE, + HELLO_MESSAGE, + IDENTIFY_MESSAGE, + KEEP_ALIVE_MESSAGE, + SEND_CHAT_MESSAGE, + RECEIVE_CHAT_MESSAGE, + ERROR_MESSAGE }; /// diff --git a/tests/unit/message_tests.cpp b/tests/unit/message_tests.cpp index bc6a8d6..8fd4624 100644 --- a/tests/unit/message_tests.cpp +++ b/tests/unit/message_tests.cpp @@ -3,11 +3,12 @@ #include #include "message.h" +#include "socket_messages.h" TEST(message_header, default_constructor) { pine::socket_messages::message_header header; - EXPECT_EQ(header.type, pine::socket_messages::message_type::invalid_message); + EXPECT_EQ(header.type, pine::socket_messages::message_type::INVALID_MESSAGE); EXPECT_EQ(header.body_size, 0); EXPECT_NE(header.id, 0ull); } @@ -15,14 +16,14 @@ TEST(message_header, default_constructor) TEST(message_header, construct_from_buffer) { std::vector buffer{ - pine::socket_messages::message_type::hello_message, // type + pine::socket_messages::message_type::HELLO_MESSAGE, // type 1, 0, 0, 0, 0, 0, 0, 0, // body size 1, 0, 0, 0, 0, 0, 0, 0, // id }; pine::socket_messages::message_header header(buffer); - EXPECT_EQ(header.type, pine::socket_messages::message_type::hello_message); + EXPECT_EQ(header.type, pine::socket_messages::message_type::HELLO_MESSAGE); EXPECT_EQ(header.body_size, 1); EXPECT_EQ(header.id, 1ull); } @@ -30,7 +31,7 @@ TEST(message_header, construct_from_buffer) TEST(message_header, parse) { std::vector buffer{ - pine::socket_messages::message_type::hello_message, // type + pine::socket_messages::message_type::HELLO_MESSAGE, // type 1, 0, 0, 0, 0, 0, 0, 0, // body size 1, 0, 0, 0, 0, 0, 0, 0, // id }; @@ -38,7 +39,7 @@ TEST(message_header, parse) pine::socket_messages::message_header header; header.parse(buffer); - EXPECT_EQ(header.type, pine::socket_messages::message_type::hello_message); + EXPECT_EQ(header.type, pine::socket_messages::message_type::HELLO_MESSAGE); EXPECT_EQ(header.body_size, 1); EXPECT_EQ(header.id, 1ull); } @@ -47,13 +48,13 @@ TEST(message_header, serialize) { pine::socket_messages::message_header header; - header.type = pine::socket_messages::message_type::hello_message; + header.type = pine::socket_messages::message_type::HELLO_MESSAGE; header.body_size = 1; header.id = 1ull; auto buffer = header.serialize(); - EXPECT_EQ(buffer[0], pine::socket_messages::message_type::hello_message); + EXPECT_EQ(buffer[0], pine::socket_messages::message_type::HELLO_MESSAGE); EXPECT_EQ(*(uint64_t*)(&buffer[1]), 1); EXPECT_EQ(*(uint64_t*)(&buffer[9]), 1ull); } @@ -61,7 +62,7 @@ TEST(message_header, serialize) TEST(message, default_constructor) { pine::socket_messages::message msg; - EXPECT_EQ(msg.header.type, pine::socket_messages::message_type::invalid_message); + EXPECT_EQ(msg.header.type, pine::socket_messages::message_type::INVALID_MESSAGE); EXPECT_EQ(msg.header.body_size, 0); EXPECT_NE(msg.header.id, 0ull); } @@ -84,3 +85,4 @@ TEST(message, serialize) auto buffer = msg.serialize(); EXPECT_EQ(buffer.size(), pine::socket_messages::message_header::size); } + From f23e012b6c12629ec16e519d98304b22ed9e9766 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 00:41:08 +0200 Subject: [PATCH 061/203] Lowercase files --- shared/include/socket_messages.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/shared/include/socket_messages.h b/shared/include/socket_messages.h index 9f88010..7e568c5 100644 --- a/shared/include/socket_messages.h +++ b/shared/include/socket_messages.h @@ -1,10 +1,9 @@ #pragma once -#include "ACKMessage.h" -#include "ErrorMessage.h" -#include "HelloMessage.h" -#include "IdentifyMessage.h" -#include "KeepAliveMessage.h" -#include "Message.h" -#include "ReceiveChatMessage.h" -#include "SendChatMessage.h" \ No newline at end of file +#include "socket_messages/ack_message.h" +#include "socket_messages/error_message.h" +#include "socket_messages/hello_message.h" +#include "socket_messages/identify_message.h" +#include "socket_messages/keep_alive_message.h" +#include "socket_messages/receive_chat_message.h" +#include "socket_messages/send_chat_message.h" From 083ca28cfabeb5aef7e847ac24cbe2af8210e4a1 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 00:42:30 +0200 Subject: [PATCH 062/203] Uppercase enum --- shared/src/socket_messages/ack_message.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shared/src/socket_messages/ack_message.cpp b/shared/src/socket_messages/ack_message.cpp index 532d1cd..c0b51fd 100644 --- a/shared/src/socket_messages/ack_message.cpp +++ b/shared/src/socket_messages/ack_message.cpp @@ -1,22 +1,24 @@ #include "socket_messages/ack_message.h" #include +#include #include #include "message.h" +#include "snowflake.h" namespace pine::socket_messages { acknowledge_message::acknowledge_message() { - header.type = message_type::acknowledge_message; + header.type = message_type::ACKNOWLEDGE_MESSAGE; header.body_size = get_body_size(); } acknowledge_message::acknowledge_message(snowflake const& id_) : id{ id_ } { - header.type = message_type::acknowledge_message; + header.type = message_type::ACKNOWLEDGE_MESSAGE; header.body_size = get_body_size(); } From fda18f69c534e4eb90d4f75c95789fe1d69af923 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 00:52:05 +0200 Subject: [PATCH 063/203] Implement error_message in .cpp --- .../include/socket_messages/error_message.h | 62 +++++-------------- shared/src/socket_messages/error_message.cpp | 43 +++++++++++++ 2 files changed, 59 insertions(+), 46 deletions(-) create mode 100644 shared/src/socket_messages/error_message.cpp diff --git a/shared/include/socket_messages/error_message.h b/shared/include/socket_messages/error_message.h index 2b7b4e1..dbe072f 100644 --- a/shared/include/socket_messages/error_message.h +++ b/shared/include/socket_messages/error_message.h @@ -4,62 +4,32 @@ #include #include -#include "Message.h" +#include "message.h" -namespace SocketMessages +namespace pine::socket_messages { - enum class ErrorCode : uint8_t + enum class error_code : uint8_t { - InvalidMessage, - InvalidMessageType, - IncompatibleAPIVersion, - WrongMessageLength, - WrongChatUsernameLength, - WrongChatMessageLength, + INVALID_MESSAGE, + INVALID_MESSAGE_TYPE, + INCOMPATIBLE_API_VERSION, + WRONG_MESSAGE_LENGTH, + WRONG_CHAT_USERNAME_LENGTH, + WRONG_CHAT_MESSAGE_LENGTH, }; - struct ErrorMessage : Message + struct error_message : message { - ErrorMessage() - { - header.messageType = MessageType::ErrorMessage; - header.bodySize = GetBodySize(); - } + error_message(); - explicit ErrorMessage(ErrorCode const& errorCode) - : m_errorCode(errorCode) - { - header.messageType = MessageType::ErrorMessage; - header.bodySize = GetBodySize(); - } + explicit error_message(error_code const& error_code); - bool ParseBody(std::vector const& buffer) override - { - if (buffer.size() != GetBodySize()) - return false; + bool parse_body(std::vector const& buffer) override; - std::memcpy(std::bit_cast(&m_errorCode), &buffer[0], sizeof(m_errorCode)); + [[nodiscard]] std::vector serialize() const override; - return true; - } + [[nodiscard]] uint64_t get_body_size() const final; - std::vector Serialize() const override - { - std::vector buffer(MessageHeader::size + GetBodySize(), 0); - std::vector headerBuffer = header.Serialize(); - - std::memcpy(&buffer[0], &headerBuffer[0], MessageHeader::size); - std::memcpy(&buffer[MessageHeader::size], std::bit_cast(&m_errorCode), sizeof(m_errorCode)); - - return buffer; - } - - uint64_t GetBodySize() const final { return sizeof(m_errorCode); } - - [[nodiscard]] constexpr ErrorCode const& GetErrorCode() const { return m_errorCode; } - constexpr void SetErrorCode(ErrorCode const& errorCode) { m_errorCode = errorCode; } - - private: - ErrorCode m_errorCode; + error_code m_error_code; }; } \ No newline at end of file diff --git a/shared/src/socket_messages/error_message.cpp b/shared/src/socket_messages/error_message.cpp new file mode 100644 index 0000000..144abb6 --- /dev/null +++ b/shared/src/socket_messages/error_message.cpp @@ -0,0 +1,43 @@ +#include "socket_messages/error_message.h" + +namespace pine::socket_messages +{ + error_message::error_message() + { + header.type = message_type::ERROR_MESSAGE; + header.body_size = get_body_size(); + } + + error_message::error_message(error_code const& error_code) + : m_error_code(error_code) + { + header.type = message_type::ERROR_MESSAGE; + header.body_size = get_body_size(); + } + + bool error_message::parse_body(std::vector const& buffer) + { + if (buffer.size() != get_body_size()) + return false; + + std::memcpy(std::bit_cast(&m_error_code), &buffer[0], sizeof(m_error_code)); + + return true; + } + + std::vector error_message::serialize() const + { + std::vector buffer(message_header::size + get_body_size(), 0); + std::vector header_buffer = header.serialize(); + + std::memcpy(&buffer[0], &header_buffer[0], message_header::size); + std::memcpy(&buffer[message_header::size], std::bit_cast(&m_error_code), sizeof(m_error_code)); + + return buffer; + } + + uint64_t error_message::get_body_size() const + { + return sizeof(m_error_code); + } +} \ No newline at end of file From 8148d7e7a1d5eab92fb8a4af53f8cff946a398bc Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 00:56:28 +0200 Subject: [PATCH 064/203] Include required files --- shared/include/socket_messages/error_message.h | 1 - shared/src/socket_messages/error_message.cpp | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/shared/include/socket_messages/error_message.h b/shared/include/socket_messages/error_message.h index dbe072f..cb545ee 100644 --- a/shared/include/socket_messages/error_message.h +++ b/shared/include/socket_messages/error_message.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include diff --git a/shared/src/socket_messages/error_message.cpp b/shared/src/socket_messages/error_message.cpp index 144abb6..db6c737 100644 --- a/shared/src/socket_messages/error_message.cpp +++ b/shared/src/socket_messages/error_message.cpp @@ -1,5 +1,11 @@ #include "socket_messages/error_message.h" +#include +#include +#include + +#include "message.h" + namespace pine::socket_messages { error_message::error_message() From cd0585750d650df78bf1b27f327d0efdff148590 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 00:58:40 +0200 Subject: [PATCH 065/203] Include type_traits --- shared/include/coroutine.h | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/include/coroutine.h b/shared/include/coroutine.h index b92d9df..53d6d39 100644 --- a/shared/include/coroutine.h +++ b/shared/include/coroutine.h @@ -3,6 +3,7 @@ #include #include #include +#include inline auto SwitchThread(std::jthread& out) { From 972b35a6515d0c7a45562e56d30dde3da91724a8 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 12:02:02 +0200 Subject: [PATCH 066/203] Implement hello_message in .cpp --- .../include/socket_messages/hello_message.h | 42 ++++--------------- shared/src/socket_messages/hello_message.cpp | 42 +++++++++++++++++++ 2 files changed, 50 insertions(+), 34 deletions(-) create mode 100644 shared/src/socket_messages/hello_message.cpp diff --git a/shared/include/socket_messages/hello_message.h b/shared/include/socket_messages/hello_message.h index 2f0fb8c..5b15e3d 100644 --- a/shared/include/socket_messages/hello_message.h +++ b/shared/include/socket_messages/hello_message.h @@ -1,48 +1,22 @@ #pragma once -#include #include #include -#include "Message.h" +#include "message.h" -namespace SocketMessages +namespace pine::socket_messages { - struct HelloMessage : Message + struct hello_message : message { - HelloMessage() - { - header.messageType = MessageType::HelloMessage; - header.bodySize = GetBodySize(); - } + hello_message(); - bool ParseBody(std::vector const& buffer) override - { - if (buffer.size() != GetBodySize()) - return false; + bool parse_body(std::vector const& buffer) override; - std::memcpy(std::bit_cast(&m_version), std::bit_cast(buffer.data()), sizeof(m_version)); + std::vector serialize() const override; - return true; - } + uint64_t get_body_size() const final; - std::vector Serialize() const override - { - std::vector buffer(MessageHeader::size + GetBodySize(), 0); - std::vector headerBuffer = header.Serialize(); - - std::memcpy(&buffer[0], &headerBuffer[0], MessageHeader::size); - std::memcpy(&buffer[MessageHeader::size], &m_version, sizeof(m_version)); - - return buffer; - } - - uint64_t GetBodySize() const final { return sizeof(m_version); } - - [[nodiscard]] constexpr uint64_t const& GetVersion() const { return m_version; } - constexpr void SetVersion(uint64_t const& version) { m_version = version; } - - private: - uint64_t m_version{ CurrentVersion }; + uint64_t m_version{ current_version }; }; } \ No newline at end of file diff --git a/shared/src/socket_messages/hello_message.cpp b/shared/src/socket_messages/hello_message.cpp new file mode 100644 index 0000000..d0e8a48 --- /dev/null +++ b/shared/src/socket_messages/hello_message.cpp @@ -0,0 +1,42 @@ +#include "socket_messages/hello_message.h" + +#include +#include +#include + +#include "message.h" + +namespace pine::socket_messages +{ + hello_message::hello_message() + { + header.type = message_type::HELLO_MESSAGE; + header.body_size = get_body_size(); + } + + bool hello_message::parse_body(std::vector const& buffer) + { + if (buffer.size() != get_body_size()) + return false; + + std::memcpy(std::bit_cast(&m_version), std::bit_cast(buffer.data()), sizeof(m_version)); + + return true; + } + + std::vector hello_message::serialize() const + { + std::vector buffer(message_header::size + get_body_size(), 0); + std::vector headerBuffer = header.serialize(); + + std::memcpy(&buffer[0], &headerBuffer[0], message_header::size); + std::memcpy(&buffer[message_header::size], &m_version, sizeof(m_version)); + + return buffer; + } + + uint64_t hello_message::get_body_size() const + { + return sizeof(m_version); + } +} \ No newline at end of file From b3ad53b33fe9ca0df9e98ab93f0d7c1c05a56935 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 12:20:23 +0200 Subject: [PATCH 067/203] Implement identify_message --- .../socket_messages/identify_message.h | 65 ++++--------------- .../src/socket_messages/identify_message.cpp | 55 ++++++++++++++++ 2 files changed, 66 insertions(+), 54 deletions(-) create mode 100644 shared/src/socket_messages/identify_message.cpp diff --git a/shared/include/socket_messages/identify_message.h b/shared/include/socket_messages/identify_message.h index 4167ae7..b9c2051 100644 --- a/shared/include/socket_messages/identify_message.h +++ b/shared/include/socket_messages/identify_message.h @@ -1,69 +1,26 @@ #pragma once -#include -#include #include +#include #include -#include "Message.h" +#include "message.h" -namespace SocketMessages +namespace pine::socket_messages { - struct IdentifyMessage : Message + struct identify_message : message { - IdentifyMessage() - { - header.messageType = MessageType:: IdentifyMessage; - header.bodySize = GetBodySize(); - } + identify_message(); + identify_message(const std::string& name); - bool ParseBody(std::vector const& buffer) override - { - if (buffer.size() < sizeof(m_usernameLength) + UsernameMinLength - || buffer.size() > sizeof(m_usernameLength) + UsernameMaxLength) - return false; + bool parse_body(std::vector const& buffer) override; - std::memcpy(std::bit_cast(&m_usernameLength), std::bit_cast(buffer.data()), sizeof(m_usernameLength)); - m_username = std::string(buffer.begin() + sizeof(m_usernameLength), buffer.end()); + std::vector serialize() const override; - header.bodySize = GetBodySize(); + uint64_t get_body_size() const final; - return true; - } + constexpr bool check_username(std::string_view const& username); - std::vector Serialize() const override - { - std::vector buffer(MessageHeader::size + GetBodySize(), 0); - std::vector headerBuffer = header.Serialize(); - - std::memcpy(&buffer[0], &headerBuffer[0], MessageHeader::size); - std::memcpy(&buffer[MessageHeader::size], &m_usernameLength, sizeof(m_usernameLength)); - std::memcpy(&buffer[MessageHeader::size + sizeof(m_usernameLength)], &m_username[0], m_usernameLength); - - return buffer; - } - - uint64_t GetBodySize() const final - { - return sizeof(m_usernameLength) + m_usernameLength; - } - - [[nodiscard]] constexpr std::string const& GetUsername() const { return m_username; } - constexpr bool SetUsername(std::string_view const& username) - { - if (username.length() < UsernameMinLength || username.length() > UsernameMaxLength) - return false; - - m_username = username; - m_usernameLength = static_cast(username.length()); - - header.bodySize = GetBodySize(); - - return true; - } - - private: - uint8_t m_usernameLength{}; - std::string m_username{}; + std::string username{}; }; } \ No newline at end of file diff --git a/shared/src/socket_messages/identify_message.cpp b/shared/src/socket_messages/identify_message.cpp new file mode 100644 index 0000000..684f984 --- /dev/null +++ b/shared/src/socket_messages/identify_message.cpp @@ -0,0 +1,55 @@ +#include "socket_messages/identify_message.h" + +namespace pine::socket_messages +{ + identify_message::identify_message() + { + header.type = message_type::IDENTIFY_MESSAGE; + header.body_size = get_body_size(); + } + + identify_message::identify_message(const std::string& name) + : username(name) + { + header.type = message_type::IDENTIFY_MESSAGE; + header.body_size = get_body_size(); + } + + bool identify_message::parse_body(std::vector const& buffer) + { + if (buffer.size() < sizeof(uint8_t) + username_min_length + || buffer.size() > sizeof(uint8_t) + username_max_length) + return false; + + username = std::string(buffer.begin() + sizeof(uint8_t), buffer.end()); + + header.body_size = get_body_size(); + + return true; + } + + std::vector identify_message::serialize() const + { + std::vector buffer(message_header::size + get_body_size(), 0); + std::vector header_buffer = header.serialize(); + + std::memcpy(&buffer[0], &header_buffer[0], message_header::size); + buffer[message_header::size] = username.length(); + std::memcpy(&buffer[message_header::size + sizeof(uint8_t)], &username[0], username.length()); + + return buffer; + } + + size_t identify_message::get_body_size() const + { + return sizeof(uint8_t) + username.length(); + } + + constexpr bool identify_message::check_username(std::string_view const& name) + { + if (username.length() < username_min_length || username.length() > username_max_length) + return false; + + return true; + } +} \ No newline at end of file From b91df74c8ee032b5fe0e54bdae403ecfa063411f Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 12:26:10 +0200 Subject: [PATCH 068/203] Implement keep_alive_message --- .../socket_messages/keep_alive_message.h | 28 +++++------------ .../socket_messages/keep_alive_message.cpp | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 shared/src/socket_messages/keep_alive_message.cpp diff --git a/shared/include/socket_messages/keep_alive_message.h b/shared/include/socket_messages/keep_alive_message.h index ccfb0b5..71d7ac3 100644 --- a/shared/include/socket_messages/keep_alive_message.h +++ b/shared/include/socket_messages/keep_alive_message.h @@ -1,34 +1,20 @@ #pragma once -#include #include #include -#include "Message.h" +#include "message.h" -namespace SocketMessages +namespace pine::socket_messages { - struct KeepAliveMessage : Message + struct keep_alive_message : message { - KeepAliveMessage() - { - header.messageType = MessageType::KeepAliveMessage; - header.bodySize = GetBodySize(); - } + keep_alive_message(); - bool ParseBody(std::vector const& buffer) override - { - if (buffer.size() != GetBodySize()) - return false; + bool parse_body(std::vector const& buffer) override; - return true; - } + std::vector serialize() const override; - std::vector Serialize() const override - { - return header.Serialize(); - } - - uint64_t GetBodySize() const final { return 0; } + uint64_t get_body_size() const final; }; } \ No newline at end of file diff --git a/shared/src/socket_messages/keep_alive_message.cpp b/shared/src/socket_messages/keep_alive_message.cpp new file mode 100644 index 0000000..f9e8da2 --- /dev/null +++ b/shared/src/socket_messages/keep_alive_message.cpp @@ -0,0 +1,31 @@ +#include "socket_messages/keep_alive_message.h" + +#include +#include + +namespace pine::socket_messages +{ + keep_alive_message::keep_alive_message() + { + header.type = message_type::KEEP_ALIVE_MESSAGE; + header.body_size = get_body_size(); + } + + bool keep_alive_message::parse_body(std::vector const& buffer) + { + if (buffer.size() != get_body_size()) + return false; + + return true; + } + + std::vector keep_alive_message::serialize() const + { + return header.serialize(); + } + + size_t keep_alive_message::get_body_size() const + { + return 0; + } +} From 50022a5a328396516db584cc596a320fde23ee91 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 12:45:22 +0200 Subject: [PATCH 069/203] Implement receive_chat_message --- .../socket_messages/receive_chat_message.h | 110 ++---------------- .../socket_messages/receive_chat_message.cpp | 78 +++++++++++++ 2 files changed, 90 insertions(+), 98 deletions(-) create mode 100644 shared/src/socket_messages/receive_chat_message.cpp diff --git a/shared/include/socket_messages/receive_chat_message.h b/shared/include/socket_messages/receive_chat_message.h index 2829e0f..72c6b67 100644 --- a/shared/include/socket_messages/receive_chat_message.h +++ b/shared/include/socket_messages/receive_chat_message.h @@ -2,113 +2,27 @@ #include #include +#include #include -#include "Message.h" +#include "message.h" -namespace SocketMessages +namespace pine::socket_messages { - struct ReceiveChatMessage : Message + struct receive_chat_message : message { - ReceiveChatMessage() - { - header.messageType = MessageType::ReceiveChatMessage; - header.bodySize = GetBodySize(); - } + receive_chat_message(); - bool ParseBody(std::vector const& buffer) override - { - if (buffer.size() < sizeof(m_authorUsernameLength) + UsernameMinLength + sizeof(m_chatMessageLength) + ChatMessageMinLength) - return false; + bool parse_body(std::vector const& buffer) override; - if (buffer.size() > sizeof(m_authorUsernameLength) + UsernameMaxLength + sizeof(m_chatMessageLength) + ChatMessageMaxLength) - return false; + std::vector serialize() const override; - std::memcpy(std::bit_cast(&m_authorUsernameLength), - buffer.data(), - sizeof(m_authorUsernameLength)); + uint64_t get_body_size() const final; - if (buffer.size() < sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength) + ChatMessageMinLength) - return false; + constexpr bool check_author_username(std::string_view const& author_username); + constexpr bool check_message_content(std::string_view const& message_content); - m_authorUsername = std::string(buffer.begin() + sizeof(m_authorUsernameLength), - buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength); - - std::memcpy(std::bit_cast(&m_chatMessageLength), - buffer.data() + sizeof(m_authorUsernameLength) + m_authorUsernameLength, - sizeof(m_chatMessageLength)); - - if (buffer.size() != sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength) + m_chatMessageLength) - return false; - - m_chatMessage = std::string(buffer.begin() + sizeof(m_authorUsernameLength) + m_authorUsernameLength + sizeof(m_chatMessageLength), - buffer.end()); - - header.bodySize = GetBodySize(); - - return true; - } - - std::vector Serialize() const override - { - std::vector buffer(MessageHeader::size + GetBodySize(), 0); - std::vector headerBuffer = header.Serialize(); - - uint8_t cursor = 0; - - std::memcpy(&buffer[cursor], &headerBuffer[0], MessageHeader::size); - cursor += MessageHeader::size; - std::memcpy(&buffer[cursor], &m_authorUsernameLength, sizeof(m_authorUsernameLength)); - cursor += sizeof(m_authorUsernameLength); - std::memcpy(&buffer[cursor], &m_authorUsername[0], m_authorUsernameLength); - cursor += m_authorUsernameLength; - std::memcpy(&buffer[cursor], &m_chatMessageLength, sizeof(m_chatMessageLength)); - cursor += sizeof(m_chatMessageLength); - std::memcpy(&buffer[cursor], &m_chatMessage[0], m_chatMessageLength); - - return buffer; - } - - uint64_t GetBodySize() const final - { - return sizeof(m_authorUsernameLength) - + m_authorUsernameLength - + sizeof(m_chatMessageLength) - + m_chatMessageLength; - } - - [[nodiscard]] constexpr std::string const& GetAuthorUsername() const { return m_authorUsername; } - constexpr bool SetAuthorUsername(std::string_view const& authorUsername) - { - if (authorUsername.length() < UsernameMinLength || authorUsername.length() > UsernameMaxLength) - return false; - - m_authorUsername = authorUsername; - m_authorUsernameLength = static_cast(authorUsername.length()); - - header.bodySize = GetBodySize(); - - return true; - } - - [[nodiscard]] constexpr std::string const& GetChatMessage() const { return m_chatMessage; } - constexpr bool SetChatMessage(std::string_view const& chatMessage) - { - if (chatMessage.length() < ChatMessageMinLength || chatMessage.length() > ChatMessageMaxLength) - return false; - - m_chatMessage = chatMessage; - m_chatMessageLength = static_cast(chatMessage.length()); - - header.bodySize = GetBodySize(); - - return true; - } - - private: - uint8_t m_authorUsernameLength{}; - std::string m_authorUsername{}; - uint16_t m_chatMessageLength{}; - std::string m_chatMessage{}; + std::string author_username{}; + std::string message_content{}; }; } \ No newline at end of file diff --git a/shared/src/socket_messages/receive_chat_message.cpp b/shared/src/socket_messages/receive_chat_message.cpp new file mode 100644 index 0000000..5827ef3 --- /dev/null +++ b/shared/src/socket_messages/receive_chat_message.cpp @@ -0,0 +1,78 @@ +#include "socket_messages/receive_chat_message.h" + +namespace pine::socket_messages +{ + receive_chat_message::receive_chat_message() + { + header.type = message_type::RECEIVE_CHAT_MESSAGE; + header.body_size = get_body_size(); + } + + bool receive_chat_message::parse_body(std::vector const& buffer) + { + if (buffer.size() < sizeof(uint8_t) + username_min_length + sizeof(uint16_t) + chat_message_min_length) + return false; + + if (buffer.size() > sizeof(uint8_t) + username_max_length + sizeof(uint16_t) + chat_message_max_length) + return false; + + uint8_t username_length = buffer[0]; + if (buffer.size() < sizeof(uint8_t) + username_length + sizeof(uint16_t) + chat_message_min_length) + return false; + + author_username = std::string(buffer.begin() + sizeof(uint8_t), buffer.begin() + sizeof(uint8_t) + username_length); + + uint16_t message_length = *reinterpret_cast(buffer.data() + sizeof(uint8_t) + username_length); + if (buffer.size() != sizeof(uint8_t) + username_length + sizeof(uint16_t) + message_length) + return false; + + message_content = std::string(buffer.begin() + sizeof(uint8_t) + username_length + sizeof(uint16_t), buffer.end()); + + header.body_size = get_body_size(); + + return true; + } + + std::vector receive_chat_message::serialize() const + { + std::vector buffer(message_header::size + get_body_size(), 0); + std::vector::iterator it = buffer.begin(); + + it = std::copy_n(header.serialize().begin(), message_header::size, it); + uint8_t author_username_length = author_username.size(); + it = std::copy_n(reinterpret_cast(&author_username_length), sizeof(uint8_t), it); + it = std::copy_n(author_username.begin(), author_username_length, it); + uint16_t message_length = message_content.size(); + it = std::copy_n(reinterpret_cast(&message_length), sizeof(uint16_t), it); + it = std::copy_n(message_content.begin(), message_length, it); + + return buffer; + } + + uint64_t receive_chat_message::get_body_size() const + { + return sizeof(uint8_t) + author_username.size() + sizeof(uint16_t) + message_content.size(); + } + + constexpr bool receive_chat_message::check_author_username(std::string_view const& username) + { + if (username.size() < username_min_length) + return false; + + if (username.size() > username_max_length) + return false; + + return true; + } + + constexpr bool receive_chat_message::check_message_content(std::string_view const& content) + { + if (content.size() < chat_message_min_length) + return false; + + if (content.size() > chat_message_max_length) + return false; + + return true; + } +} \ No newline at end of file From 2c1b1d55dff2412e0503300fea91e179ff723a44 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 12:46:29 +0200 Subject: [PATCH 070/203] Clean includes --- shared/include/socket_messages/receive_chat_message.h | 1 - shared/src/socket_messages/receive_chat_message.cpp | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/shared/include/socket_messages/receive_chat_message.h b/shared/include/socket_messages/receive_chat_message.h index 72c6b67..19e7dcb 100644 --- a/shared/include/socket_messages/receive_chat_message.h +++ b/shared/include/socket_messages/receive_chat_message.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include diff --git a/shared/src/socket_messages/receive_chat_message.cpp b/shared/src/socket_messages/receive_chat_message.cpp index 5827ef3..2868765 100644 --- a/shared/src/socket_messages/receive_chat_message.cpp +++ b/shared/src/socket_messages/receive_chat_message.cpp @@ -1,5 +1,9 @@ #include "socket_messages/receive_chat_message.h" +#include +#include +#include + namespace pine::socket_messages { receive_chat_message::receive_chat_message() From a84e781be7da7736635ccfcf746c1cee53798db9 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 12:56:38 +0200 Subject: [PATCH 071/203] Use std::copy_n instead of std::memcpy --- shared/src/socket_messages/ack_message.cpp | 8 ++++---- shared/src/socket_messages/error_message.cpp | 9 ++++----- shared/src/socket_messages/hello_message.cpp | 6 +++--- shared/src/socket_messages/identify_message.cpp | 15 +++++++++++---- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/shared/src/socket_messages/ack_message.cpp b/shared/src/socket_messages/ack_message.cpp index c0b51fd..25332df 100644 --- a/shared/src/socket_messages/ack_message.cpp +++ b/shared/src/socket_messages/ack_message.cpp @@ -28,7 +28,7 @@ namespace pine::socket_messages return false; uint64_t new_id{}; - std::memcpy(std::bit_cast(&new_id), std::bit_cast(buffer.data()), sizeof(new_id)); + std::copy_n(buffer.begin(), sizeof(new_id), reinterpret_cast(&new_id)); id = snowflake(new_id); return true; @@ -37,11 +37,11 @@ namespace pine::socket_messages std::vector acknowledge_message::serialize() const { std::vector buffer(message_header::size + get_body_size(), 0); - std::vector header_buffer = header.serialize(); + std::vector::iterator it = buffer.begin(); - std::memcpy(&buffer[0], &header_buffer[0], message_header::size); + it = std::copy_n(header.serialize().begin(), message_header::size, it); auto id_ = static_cast(id); - std::memcpy(&buffer[message_header::size], &id_, sizeof(id_)); + it = std::copy_n(&id_, sizeof(id_), it); return buffer; } diff --git a/shared/src/socket_messages/error_message.cpp b/shared/src/socket_messages/error_message.cpp index db6c737..57e99e3 100644 --- a/shared/src/socket_messages/error_message.cpp +++ b/shared/src/socket_messages/error_message.cpp @@ -1,6 +1,5 @@ #include "socket_messages/error_message.h" -#include #include #include @@ -26,7 +25,7 @@ namespace pine::socket_messages if (buffer.size() != get_body_size()) return false; - std::memcpy(std::bit_cast(&m_error_code), &buffer[0], sizeof(m_error_code)); + m_error_code = static_cast(buffer[0]); return true; } @@ -34,10 +33,10 @@ namespace pine::socket_messages std::vector error_message::serialize() const { std::vector buffer(message_header::size + get_body_size(), 0); - std::vector header_buffer = header.serialize(); + std::vector::iterator it = buffer.begin(); - std::memcpy(&buffer[0], &header_buffer[0], message_header::size); - std::memcpy(&buffer[message_header::size], std::bit_cast(&m_error_code), sizeof(m_error_code)); + it = std::copy_n(header.serialize().begin(), message_header::size, it); + it = std::copy_n(&m_error_code, sizeof(m_error_code), it); return buffer; } diff --git a/shared/src/socket_messages/hello_message.cpp b/shared/src/socket_messages/hello_message.cpp index d0e8a48..4853f29 100644 --- a/shared/src/socket_messages/hello_message.cpp +++ b/shared/src/socket_messages/hello_message.cpp @@ -27,10 +27,10 @@ namespace pine::socket_messages std::vector hello_message::serialize() const { std::vector buffer(message_header::size + get_body_size(), 0); - std::vector headerBuffer = header.serialize(); + std::vector::iterator it = buffer.begin(); - std::memcpy(&buffer[0], &headerBuffer[0], message_header::size); - std::memcpy(&buffer[message_header::size], &m_version, sizeof(m_version)); + it = std::copy_n(header.serialize().begin(), message_header::size, it); + it = std::copy_n(std::bit_cast(&m_version), sizeof(m_version), it); return buffer; } diff --git a/shared/src/socket_messages/identify_message.cpp b/shared/src/socket_messages/identify_message.cpp index 684f984..65fe19a 100644 --- a/shared/src/socket_messages/identify_message.cpp +++ b/shared/src/socket_messages/identify_message.cpp @@ -1,5 +1,11 @@ #include "socket_messages/identify_message.h" +#include +#include +#include + +#include "message.h" + namespace pine::socket_messages { identify_message::identify_message() @@ -31,11 +37,12 @@ namespace pine::socket_messages std::vector identify_message::serialize() const { std::vector buffer(message_header::size + get_body_size(), 0); - std::vector header_buffer = header.serialize(); + std::vector::iterator it = buffer.begin(); - std::memcpy(&buffer[0], &header_buffer[0], message_header::size); - buffer[message_header::size] = username.length(); - std::memcpy(&buffer[message_header::size + sizeof(uint8_t)], &username[0], username.length()); + it = std::copy_n(header.serialize().begin(), message_header::size, it); + uint8_t username_length = username.size(); + it = std::copy_n(reinterpret_cast(&username_length), sizeof(uint8_t), it); + it = std::copy_n(reinterpret_cast(username.c_str()), username_length, it); return buffer; } From 4390d762a19b9338f9a4c4ebf51b53905e6da900 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 13:11:14 +0200 Subject: [PATCH 072/203] Implement send_chat_message --- .../socket_messages/send_chat_message.h | 75 +++---------------- .../src/socket_messages/send_chat_message.cpp | 59 +++++++++++++++ 2 files changed, 69 insertions(+), 65 deletions(-) create mode 100644 shared/src/socket_messages/send_chat_message.cpp diff --git a/shared/include/socket_messages/send_chat_message.h b/shared/include/socket_messages/send_chat_message.h index cfa3dfc..decce17 100644 --- a/shared/include/socket_messages/send_chat_message.h +++ b/shared/include/socket_messages/send_chat_message.h @@ -1,80 +1,25 @@ #pragma once -#include -#include #include +#include #include -#include "Message.h" -#include "User.h" +#include "message.h" -namespace SocketMessages +namespace pine::socket_messages { - struct SendChatMessage : Message + struct send_chat_message : message { - SendChatMessage() - { - header.messageType = MessageType::SendChatMessage; - header.bodySize = GetBodySize(); - } + send_chat_message(); - bool ParseBody(std::vector const& buffer) override - { - if (buffer.size() < sizeof(m_chatMessageLength) + ChatMessageMinLength - || buffer.size() > sizeof (m_chatMessageLength) + ChatMessageMaxLength) - return false; + bool parse_body(std::vector const& buffer) override; - std::memcpy(std::bit_cast(&m_chatMessageLength), - buffer.data(), sizeof(m_chatMessageLength)); + std::vector serialize() const override; - if (buffer.size() != sizeof(m_chatMessageLength) + m_chatMessageLength) - return false; + uint64_t constexpr get_body_size() const final; - m_chatMessage = std::string(buffer.begin() + sizeof(m_chatMessageLength), - buffer.begin() + sizeof(m_chatMessageLength) + m_chatMessageLength); + constexpr bool check_message_content(std::string_view const& content); - header.bodySize = GetBodySize(); - - return true; - } - - std::vector Serialize() const override - { - std::vector buffer(MessageHeader::size + GetBodySize(), 0); - std::vector headerBuffer = header.Serialize(); - - uint8_t cursor = 0; - - std::memcpy(&buffer[cursor], &headerBuffer[0], MessageHeader::size); - cursor += MessageHeader::size; - std::memcpy(&buffer[cursor], &m_chatMessageLength, sizeof(m_chatMessageLength)); - cursor += sizeof(m_chatMessageLength); - std::memcpy(&buffer[cursor], &m_chatMessage[0], m_chatMessageLength); - - return buffer; - } - - uint64_t constexpr GetBodySize() const final - { - return sizeof(m_chatMessageLength) + m_chatMessageLength; - } - - [[nodiscard]] constexpr std::string const& GetChatMessage() const { return m_chatMessage; } - constexpr bool SetChatMessage(std::string_view const& chatMessage) - { - if (chatMessage.size() > ChatMessageMaxLength || chatMessage.size() < ChatMessageMinLength) - return false; - - m_chatMessage = chatMessage; - m_chatMessageLength = static_cast(chatMessage.size()); - - header.bodySize = GetBodySize(); - - return true; - } - - private: - uint16_t m_chatMessageLength{}; - std::string m_chatMessage{}; + std::string message_content{}; }; } \ No newline at end of file diff --git a/shared/src/socket_messages/send_chat_message.cpp b/shared/src/socket_messages/send_chat_message.cpp new file mode 100644 index 0000000..d6f1375 --- /dev/null +++ b/shared/src/socket_messages/send_chat_message.cpp @@ -0,0 +1,59 @@ +#include "socket_messages/send_chat_message.h" + +#include +#include +#include + +#include "message.h" + +namespace pine::socket_messages +{ + send_chat_message::send_chat_message() + { + header.type = message_type::SEND_CHAT_MESSAGE; + header.body_size = get_body_size(); + } + + bool send_chat_message::parse_body(std::vector const& buffer) + { + if (buffer.size() < sizeof(uint16_t) + chat_message_min_length + || buffer.size() > sizeof(uint16_t) + chat_message_max_length) + return false; + + uint16_t message_length = *reinterpret_cast(buffer.data()); + if (buffer.size() != sizeof(uint16_t) + message_length) + return false; + + message_content = std::string(buffer.begin() + sizeof(uint16_t), buffer.end()); + + header.body_size = get_body_size(); + + return true; + } + + std::vector send_chat_message::serialize() const + { + std::vector buffer(message_header::size + sizeof(uint16_t) + message_content.size()); + std::vector::iterator it = buffer.begin(); + + it = std::copy_n(header.serialize().begin(), message_header::size, it); + uint16_t message_length = static_cast(message_content.size()); + it = std::copy_n(reinterpret_cast(&message_length), sizeof(uint16_t), it); + it = std::copy_n(message_content.begin(), message_content.size(), it); + + return buffer; + } + + size_t send_chat_message::get_body_size() const + { + return sizeof(uint16_t) + message_content.size(); + } + + constexpr bool send_chat_message::check_message_content(std::string_view const& content) + { + if (content.size() < chat_message_min_length || content.size() > chat_message_max_length) + return false; + + return true; + } +} \ No newline at end of file From 2721c173caac0d4e6e576ad8a3ffa05535df4bb8 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 13:27:08 +0200 Subject: [PATCH 073/203] Auto include new source files --- tests/unit/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 4ddc584..8d5acc8 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -1,9 +1,11 @@ include_directories() +file (GLOB_RECURSE SOURCES *.cpp) + add_executable( unit_tests - "snowflake_tests.cpp" - "message_tests.cpp") + ${SOURCES} +) target_link_libraries(unit_tests PRIVATE shared) target_link_libraries(unit_tests PRIVATE GTest::gtest GTest::gtest_main GTest::gmock GTest::gmock_main) From 651e6a5d0ace3bf112e3772c9be75720c5bd9b4d Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 13:27:40 +0200 Subject: [PATCH 074/203] Move tests main function to new file --- tests/unit/snowflake_tests.cpp | 6 ------ tests/unit/unit_tests.cpp | 7 +++++++ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 tests/unit/unit_tests.cpp diff --git a/tests/unit/snowflake_tests.cpp b/tests/unit/snowflake_tests.cpp index a6014c5..c691f67 100644 --- a/tests/unit/snowflake_tests.cpp +++ b/tests/unit/snowflake_tests.cpp @@ -72,9 +72,3 @@ TEST(snowflake, stream_operator) stream << snowflake; EXPECT_EQ(stream.str(), "1"); } - -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} \ No newline at end of file diff --git a/tests/unit/unit_tests.cpp b/tests/unit/unit_tests.cpp new file mode 100644 index 0000000..8f26102 --- /dev/null +++ b/tests/unit/unit_tests.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file From 0239d12c03e3c995e6043783dd66f3f5bac822f0 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 13:28:42 +0200 Subject: [PATCH 075/203] Remove unused header --- tests/unit/message_tests.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/message_tests.cpp b/tests/unit/message_tests.cpp index 8fd4624..9cbfead 100644 --- a/tests/unit/message_tests.cpp +++ b/tests/unit/message_tests.cpp @@ -3,7 +3,6 @@ #include #include "message.h" -#include "socket_messages.h" TEST(message_header, default_constructor) { From dd1e43de61ac2f30ca5b7bf8b9f8d734e7239abc Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 13:29:01 +0200 Subject: [PATCH 076/203] Fix function signature --- shared/src/socket_messages/send_chat_message.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/socket_messages/send_chat_message.cpp b/shared/src/socket_messages/send_chat_message.cpp index d6f1375..8c0e9c5 100644 --- a/shared/src/socket_messages/send_chat_message.cpp +++ b/shared/src/socket_messages/send_chat_message.cpp @@ -44,7 +44,7 @@ namespace pine::socket_messages return buffer; } - size_t send_chat_message::get_body_size() const + constexpr uint64_t send_chat_message::get_body_size() const { return sizeof(uint16_t) + message_content.size(); } From 48546c3d3d1c33fefe0059ad46769d458ca67de3 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 13:29:19 +0200 Subject: [PATCH 077/203] Cast error_code* to uint8_t* --- shared/src/socket_messages/error_message.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/src/socket_messages/error_message.cpp b/shared/src/socket_messages/error_message.cpp index 57e99e3..72e9503 100644 --- a/shared/src/socket_messages/error_message.cpp +++ b/shared/src/socket_messages/error_message.cpp @@ -36,7 +36,7 @@ namespace pine::socket_messages std::vector::iterator it = buffer.begin(); it = std::copy_n(header.serialize().begin(), message_header::size, it); - it = std::copy_n(&m_error_code, sizeof(m_error_code), it); + it = std::copy_n(reinterpret_cast(&m_error_code), sizeof(m_error_code), it); return buffer; } From 10a283953d206320ac020c3678bcbd96766719be Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 17:44:53 +0200 Subject: [PATCH 078/203] Fix acknowledged_message id padding --- shared/include/socket_messages/ack_message.h | 2 +- shared/src/socket_messages/ack_message.cpp | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/shared/include/socket_messages/ack_message.h b/shared/include/socket_messages/ack_message.h index c1d6651..9d9e66d 100644 --- a/shared/include/socket_messages/ack_message.h +++ b/shared/include/socket_messages/ack_message.h @@ -20,6 +20,6 @@ namespace pine::socket_messages [[nodiscard]] uint64_t get_body_size() const final; - snowflake id{}; + snowflake acknowledged_message_id{}; }; } diff --git a/shared/src/socket_messages/ack_message.cpp b/shared/src/socket_messages/ack_message.cpp index 25332df..007e30a 100644 --- a/shared/src/socket_messages/ack_message.cpp +++ b/shared/src/socket_messages/ack_message.cpp @@ -15,8 +15,8 @@ namespace pine::socket_messages header.body_size = get_body_size(); } - acknowledge_message::acknowledge_message(snowflake const& id_) - : id{ id_ } + acknowledge_message::acknowledge_message(snowflake const& id) + : acknowledged_message_id{ id } { header.type = message_type::ACKNOWLEDGE_MESSAGE; header.body_size = get_body_size(); @@ -29,7 +29,7 @@ namespace pine::socket_messages uint64_t new_id{}; std::copy_n(buffer.begin(), sizeof(new_id), reinterpret_cast(&new_id)); - id = snowflake(new_id); + acknowledged_message_id = snowflake(new_id); return true; } @@ -40,8 +40,9 @@ namespace pine::socket_messages std::vector::iterator it = buffer.begin(); it = std::copy_n(header.serialize().begin(), message_header::size, it); - auto id_ = static_cast(id); - it = std::copy_n(&id_, sizeof(id_), it); + uint64_t id_ = acknowledged_message_id; + + it = std::copy_n(reinterpret_cast(&id_), sizeof(id_), it); return buffer; } From c80aff74bdbdfc07d3bd6448c3178f8d29447ab5 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 17:45:01 +0200 Subject: [PATCH 079/203] Add tests for ack message --- .../ack_message_tests.cpp | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/unit/socket_messages_tests/ack_message_tests.cpp diff --git a/tests/unit/socket_messages_tests/ack_message_tests.cpp b/tests/unit/socket_messages_tests/ack_message_tests.cpp new file mode 100644 index 0000000..039690b --- /dev/null +++ b/tests/unit/socket_messages_tests/ack_message_tests.cpp @@ -0,0 +1,61 @@ +#include + +#include +#include + +#include +#include +#include + + +TEST(acknowledge_message, default_constructor) +{ + pine::socket_messages::acknowledge_message msg; + EXPECT_EQ(msg.header.type, pine::socket_messages::message_type::ACKNOWLEDGE_MESSAGE); + EXPECT_EQ(msg.header.body_size, sizeof(pine::snowflake::value)); +} + +TEST(acknowledge_message, construct_from_id) +{ + pine::snowflake id; + pine::socket_messages::acknowledge_message msg(id); + + EXPECT_EQ(msg.header.type, pine::socket_messages::message_type::ACKNOWLEDGE_MESSAGE); + EXPECT_EQ(msg.header.body_size, sizeof(pine::snowflake::value)); + EXPECT_EQ(msg.acknowledged_message_id, id); +} + +TEST(acknowledge_message, parse_body) +{ + pine::socket_messages::acknowledge_message msg; + + std::vector body(0, 0); + EXPECT_FALSE(msg.parse_body(body)); + + body.resize(sizeof(pine::snowflake::value), 0); + body[0] = 0x01; + EXPECT_TRUE(msg.parse_body(body)); + + pine::snowflake id; + id = 0x01; + EXPECT_EQ(msg.acknowledged_message_id, id); +} + +TEST(acknowledge_message, serialize) +{ + pine::socket_messages::acknowledge_message msg; + msg.acknowledged_message_id = 0x01; + + std::vector buffer = msg.serialize(); + + EXPECT_EQ(buffer.size(), pine::socket_messages::message_header::size + sizeof(pine::snowflake::value)); + + pine::socket_messages::message_header header; + header.parse(buffer); + EXPECT_EQ(header.type, pine::socket_messages::message_type::ACKNOWLEDGE_MESSAGE); + EXPECT_EQ(header.body_size, sizeof(pine::snowflake::value)); + + uint64_t id{}; + std::copy_n(buffer.begin() + pine::socket_messages::message_header::size, sizeof(id), reinterpret_cast(&id)); + EXPECT_EQ(id, 0x01); +} From 8ccb0843e02510833dedfd40d467d7f579d85903 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 17:52:53 +0200 Subject: [PATCH 080/203] Remove member prefix --- shared/include/socket_messages/error_message.h | 2 +- shared/src/socket_messages/error_message.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shared/include/socket_messages/error_message.h b/shared/include/socket_messages/error_message.h index cb545ee..27ce161 100644 --- a/shared/include/socket_messages/error_message.h +++ b/shared/include/socket_messages/error_message.h @@ -29,6 +29,6 @@ namespace pine::socket_messages [[nodiscard]] uint64_t get_body_size() const final; - error_code m_error_code; + error_code error; }; } \ No newline at end of file diff --git a/shared/src/socket_messages/error_message.cpp b/shared/src/socket_messages/error_message.cpp index 72e9503..f1e0649 100644 --- a/shared/src/socket_messages/error_message.cpp +++ b/shared/src/socket_messages/error_message.cpp @@ -14,7 +14,7 @@ namespace pine::socket_messages } error_message::error_message(error_code const& error_code) - : m_error_code(error_code) + : error(error_code) { header.type = message_type::ERROR_MESSAGE; header.body_size = get_body_size(); @@ -25,7 +25,7 @@ namespace pine::socket_messages if (buffer.size() != get_body_size()) return false; - m_error_code = static_cast(buffer[0]); + error = static_cast(buffer[0]); return true; } @@ -36,13 +36,13 @@ namespace pine::socket_messages std::vector::iterator it = buffer.begin(); it = std::copy_n(header.serialize().begin(), message_header::size, it); - it = std::copy_n(reinterpret_cast(&m_error_code), sizeof(m_error_code), it); + it = std::copy_n(reinterpret_cast(&error), sizeof(error), it); return buffer; } uint64_t error_message::get_body_size() const { - return sizeof(m_error_code); + return sizeof(error); } } \ No newline at end of file From 6926f16d0c838d8be39ae24ee82048f3b7469e59 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 17:53:07 +0200 Subject: [PATCH 081/203] Add test body size --- tests/unit/socket_messages_tests/ack_message_tests.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/unit/socket_messages_tests/ack_message_tests.cpp b/tests/unit/socket_messages_tests/ack_message_tests.cpp index 039690b..2c45d5c 100644 --- a/tests/unit/socket_messages_tests/ack_message_tests.cpp +++ b/tests/unit/socket_messages_tests/ack_message_tests.cpp @@ -59,3 +59,9 @@ TEST(acknowledge_message, serialize) std::copy_n(buffer.begin() + pine::socket_messages::message_header::size, sizeof(id), reinterpret_cast(&id)); EXPECT_EQ(id, 0x01); } + +TEST(acknowledge_message, get_body_size) +{ + pine::socket_messages::acknowledge_message msg; + EXPECT_EQ(msg.get_body_size(), sizeof(pine::snowflake::value)); +} \ No newline at end of file From f6658eac8d869734474f47bcd48c8719ceff8ef4 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 17:53:18 +0200 Subject: [PATCH 082/203] Add tests for error_message --- .../error_message_tests.cpp | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/unit/socket_messages_tests/error_message_tests.cpp diff --git a/tests/unit/socket_messages_tests/error_message_tests.cpp b/tests/unit/socket_messages_tests/error_message_tests.cpp new file mode 100644 index 0000000..4befdb0 --- /dev/null +++ b/tests/unit/socket_messages_tests/error_message_tests.cpp @@ -0,0 +1,62 @@ +#include + +#include +#include + +#include +#include + +TEST(error_message, default_constructor) +{ + pine::socket_messages::error_message msg; + EXPECT_EQ(msg.header.type, pine::socket_messages::message_type::ERROR_MESSAGE); + EXPECT_EQ(msg.header.body_size, sizeof(pine::socket_messages::error_code)); +} + +TEST(error_message, construct_from_error_code) +{ + pine::socket_messages::error_message msg(pine::socket_messages::error_code::INVALID_MESSAGE); + + EXPECT_EQ(msg.header.type, pine::socket_messages::message_type::ERROR_MESSAGE); + EXPECT_EQ(msg.header.body_size, sizeof(pine::socket_messages::error_code)); + EXPECT_EQ(msg.error, pine::socket_messages::error_code::INVALID_MESSAGE); +} + +TEST(error_message, parse_body) +{ + pine::socket_messages::error_message msg; + + std::vector body(0, 0); + EXPECT_FALSE(msg.parse_body(body)); + + body.resize(sizeof(pine::socket_messages::error_code), 0); + body[0] = 0x01; + EXPECT_TRUE(msg.parse_body(body)); + + EXPECT_EQ(msg.error, pine::socket_messages::error_code::INVALID_MESSAGE_TYPE); +} + +TEST(error_message, serialize) +{ + pine::socket_messages::error_message msg; + msg.error = pine::socket_messages::error_code::INVALID_MESSAGE_TYPE; + + std::vector buffer = msg.serialize(); + + EXPECT_EQ(buffer.size(), pine::socket_messages::message_header::size + sizeof(pine::socket_messages::error_code)); + + pine::socket_messages::message_header header; + header.parse(buffer); + EXPECT_EQ(header.type, pine::socket_messages::message_type::ERROR_MESSAGE); + EXPECT_EQ(header.body_size, sizeof(pine::socket_messages::error_code)); + + uint8_t error{}; + std::copy_n(buffer.begin() + pine::socket_messages::message_header::size, sizeof(error), &error); + EXPECT_EQ(error, 0x01); +} + +TEST(error_message, get_body_size) +{ + pine::socket_messages::error_message msg; + EXPECT_EQ(msg.get_body_size(), sizeof(pine::socket_messages::error_code)); +} \ No newline at end of file From e40d2629cd3c581069ed3029ca31945f0ba127ce Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 18:28:25 +0200 Subject: [PATCH 083/203] Remove Visual Studio project files --- .../ChatIntegrationsTests.cpp | 53 ----- .../EstablishConnection.h | 24 --- .../EstablishConnectionIntegrationTests.cpp | 79 -------- .../ServerIntegrationTests.vcxproj | 180 ----------------- .../ServerIntegrationTests.vcxproj.filters | 30 --- .../ServerUnitTest/ParseMessageUnitTests.cpp | 135 ------------- .../SerializeMessageUnitTests.cpp | 180 ----------------- tests/ServerUnitTest/ServerUnitTest.vcxproj | 185 ------------------ .../ServerUnitTest.vcxproj.filters | 25 --- 9 files changed, 891 deletions(-) delete mode 100644 tests/ServerIntegrationTests/ChatIntegrationsTests.cpp delete mode 100644 tests/ServerIntegrationTests/EstablishConnection.h delete mode 100644 tests/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp delete mode 100644 tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj delete mode 100644 tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters delete mode 100644 tests/ServerUnitTest/ParseMessageUnitTests.cpp delete mode 100644 tests/ServerUnitTest/SerializeMessageUnitTests.cpp delete mode 100644 tests/ServerUnitTest/ServerUnitTest.vcxproj delete mode 100644 tests/ServerUnitTest/ServerUnitTest.vcxproj.filters diff --git a/tests/ServerIntegrationTests/ChatIntegrationsTests.cpp b/tests/ServerIntegrationTests/ChatIntegrationsTests.cpp deleted file mode 100644 index 556ca9b..0000000 --- a/tests/ServerIntegrationTests/ChatIntegrationsTests.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "CppUnitTest.h" - -#include - -#include "Connection.h" -#include "Coroutine.h" -#include "EstablishConnection.h" -#include "Message.h" - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -namespace ServerIntegrationTests -{ - TEST_CLASS(ChatIntegrationTests) - { - TEST_METHOD(SendChatMessage) - { - std::mutex mutex; - - [&mutex]() -> AsyncTask - { - std::lock_guard lock{ mutex }; - - ClientConnection sender; - ClientConnection receiver; - - co_await EstablishConnection(sender); - co_await EstablishConnection(receiver); - - auto sendChat = std::make_shared(); - sendChat->SetChatMessage("Hello world!"); - - co_await sender.SendMessage(sendChat); - - std::shared_ptr receivedChat = co_await receiver.ReceiveMessage(); - - Assert::AreEqual(static_cast(receivedChat->header.messageType), - static_cast(SocketMessages::MessageType::ReceiveChatMessage), - L"Receiver didn't receive message"); - - Assert::AreEqual(std::dynamic_pointer_cast(receivedChat)->GetChatMessage(), - std::string("Hello world!"), - L"Receiver didn't receive correct message"); - - Assert::AreEqual(std::dynamic_pointer_cast(receivedChat)->GetAuthorUsername(), - sender.GetUser().GetUsername(), - L"Received message from wrong username"); - }(); - - std::lock_guard lock{ mutex }; - } - }; -} \ No newline at end of file diff --git a/tests/ServerIntegrationTests/EstablishConnection.h b/tests/ServerIntegrationTests/EstablishConnection.h deleted file mode 100644 index 317b256..0000000 --- a/tests/ServerIntegrationTests/EstablishConnection.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "Connection.h" - -namespace ServerIntegrationTests -{ - AsyncTask EstablishConnection(ClientConnection& client) - { - client.Connect(); - std::vector validationToken = co_await client.ReceiveRawMessage(sizeof(uint64_t)); - *std::bit_cast(validationToken.data()) ^= 0xF007CAFEC0C0CACA; - co_await client.SendRawMessage(validationToken); - std::shared_ptr validationResponse = co_await client.ReceiveMessage(); - - auto helloMessage = std::make_shared(); - co_await client.SendMessage(helloMessage); - std::shared_ptr helloResponse = co_await client.ReceiveMessage(); - - auto identifyMessage = std::make_shared(); - identifyMessage->SetUsername("Username"); - co_await client.SendMessage(identifyMessage); - std::shared_ptr identifyResponse = co_await client.ReceiveMessage(); - } -} \ No newline at end of file diff --git a/tests/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp b/tests/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp deleted file mode 100644 index 9e81fbd..0000000 --- a/tests/ServerIntegrationTests/EstablishConnectionIntegrationTests.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "CppUnitTest.h" - -#include - -#include "Connection.h" -#include "Coroutine.h" -#include "Message.h" - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -namespace ServerIntegrationTests -{ - TEST_CLASS(EstablishConnectionIntegrationTests) - { - public: - - - - TEST_METHOD(EstablishSingleConnection) - { - std::mutex mutex; - - [&mutex]() -> AsyncTask - { - ClientConnection client; - - Assert::IsTrue(client.Connect()); - - std::lock_guard lock{ mutex }; - - - - - std::vector validationToken = co_await client.ReceiveRawMessage(sizeof(uint64_t)); - - Assert::AreEqual(sizeof(uint64_t), validationToken.size(), L"Wrong received token size"); - - *std::bit_cast(validationToken.data()) ^= 0xF007CAFEC0C0CACA; - - co_await client.SendRawMessage(validationToken); - - std::shared_ptr validationResponse = co_await client.ReceiveMessage(); - - Assert::AreEqual(static_cast(validationResponse->header.messageType), - static_cast(SocketMessages::MessageType::HelloMessage), - L"Wrong message type received after validation"); - - - - - auto helloMessage = std::make_shared(); - - co_await client.SendMessage(helloMessage); - - std::shared_ptr helloResponse = co_await client.ReceiveMessage(); - - Assert::AreEqual(static_cast(helloResponse->header.messageType), - static_cast(SocketMessages::MessageType::AcknowledgeMessage), - L"Wrong message type received after hello"); - - - - - auto identifyMessage = std::make_shared(); - identifyMessage->SetUsername("Username"); - - co_await client.SendMessage(identifyMessage); - - std::shared_ptr identifyResponse = co_await client.ReceiveMessage(); - - Assert::AreEqual(static_cast(identifyResponse->header.messageType), - static_cast(SocketMessages::MessageType::AcknowledgeMessage), - L"Wrong message type received after hello"); - }(); - - std::lock_guard lock{ mutex }; - } - }; -} diff --git a/tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj b/tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj deleted file mode 100644 index 1d5c2d1..0000000 --- a/tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj +++ /dev/null @@ -1,180 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 17.0 - {2FF51663-B837-42E3-9E00-31B1AB857920} - Win32Proj - ServerIntegrationTests - 10.0 - NativeUnitTestProject - - - - DynamicLibrary - true - v143 - Unicode - false - - - DynamicLibrary - false - v143 - true - Unicode - false - - - DynamicLibrary - true - v143 - Unicode - false - - - DynamicLibrary - false - v143 - true - Unicode - false - - - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - NotUsing - Level3 - true - $(SolutionDir)Server;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - _DEBUG;%(PreprocessorDefinitions) - true - pch.h - stdcpplatest - - - Windows - $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - - - - - NotUsing - Level3 - true - $(SolutionDir)Server;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;%(PreprocessorDefinitions) - true - pch.h - stdcpplatest - - - Windows - $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - - - - - NotUsing - Level3 - true - true - true - $(SolutionDir)Server;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;%(PreprocessorDefinitions) - true - pch.h - stdcpplatest - - - Windows - true - true - $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - - - - - NotUsing - Level3 - true - true - true - $(SolutionDir)Server;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - NDEBUG;%(PreprocessorDefinitions) - true - pch.h - stdcpplatest - - - Windows - true - true - $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - - - - - - - - - {f11a6291-405c-4a94-ab56-f0027fdd4b4d} - - - {ffa917e0-c449-48c7-bdf3-9877cbd96b46} - - - - - - - - - \ No newline at end of file diff --git a/tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters b/tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters deleted file mode 100644 index 68ad41a..0000000 --- a/tests/ServerIntegrationTests/ServerIntegrationTests.vcxproj.filters +++ /dev/null @@ -1,30 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - - - Header Files - - - \ No newline at end of file diff --git a/tests/ServerUnitTest/ParseMessageUnitTests.cpp b/tests/ServerUnitTest/ParseMessageUnitTests.cpp deleted file mode 100644 index f8f7b3a..0000000 --- a/tests/ServerUnitTest/ParseMessageUnitTests.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "CppUnitTest.h" - -#include -#include - -#include "Snowflake.h" -#include "SocketMessages.h" - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -namespace ServerUnitTest -{ - TEST_CLASS(ParseMessages) - { - public: - - TEST_METHOD(ParseACKMessage) - { - Snowflake messageId{}; - - SocketMessages::AcknowledgeMessage ackMessage{ messageId }; - - std::vector messageBuffer = ackMessage.Serialize(); - std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); - - Assert::IsFalse(ackMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); - Assert::IsTrue(ackMessage.ParseBody(buffer), L"Valid data can't be parsed"); - - Assert::AreEqual( - static_cast(messageId), - static_cast(ackMessage.GetAcknowledgedMessageId()), - L"Acknowledged message Id id wrong" - ); - } - - TEST_METHOD(ParseErrorMessage) - { - SocketMessages::ErrorCode errorCode{ SocketMessages::ErrorCode::InvalidMessage }; - - SocketMessages::ErrorMessage errorMessage{ errorCode }; - - std::vector messageBuffer = errorMessage.Serialize(); - std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); - - Assert::IsFalse(errorMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); - Assert::IsTrue(errorMessage.ParseBody(buffer), L"Valid data can't be parsed"); - - Assert::AreEqual( - static_cast(errorCode), - static_cast(errorMessage.GetErrorCode()), - L"Error code is wrong" - ); - } - - TEST_METHOD(ParseHelloMessage) - { - SocketMessages::HelloMessage helloMessage{}; - - std::vector messageBuffer = helloMessage.Serialize(); - std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); - - Assert::IsFalse(helloMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); - Assert::IsTrue(helloMessage.ParseBody(buffer), L"Valid data can't be parsed"); - - Assert::AreEqual( - static_cast(CurrentVersion), - static_cast(helloMessage.GetVersion()), - L"API version is wrong" - ); - } - - TEST_METHOD(ParseIdentifyMessage) - { - std::string username = "Username"; - - SocketMessages::IdentifyMessage identifyMessage{}; - - identifyMessage.SetUsername(username); - - std::vector messageBuffer = identifyMessage.Serialize(); - std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); - - Assert::IsFalse(identifyMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); - Assert::IsTrue(identifyMessage.ParseBody(buffer), L"Valid data can't be parsed"); - - Assert::AreEqual(username, identifyMessage.GetUsername(), L"Username is wrong"); - } - - TEST_METHOD(ParseKeepAliveMessage) - { - SocketMessages::KeepAliveMessage keepAliveMessage{}; - - std::vector messageBuffer = keepAliveMessage.Serialize(); - std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); - - Assert::IsFalse(keepAliveMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); - Assert::IsTrue(keepAliveMessage.ParseBody(buffer), L"Valid data can't be parsed"); - } - - TEST_METHOD(ParseReceiveChatMessage) - { - std::string authorUsername = "Username"; - std::string chatMessage = "Message"; - - SocketMessages::ReceiveChatMessage receiveChatMessage{}; - receiveChatMessage.SetAuthorUsername(authorUsername); - receiveChatMessage.SetChatMessage(chatMessage); - - std::vector messageBuffer = receiveChatMessage.Serialize(); - std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); - - Assert::IsFalse(receiveChatMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); - Assert::IsTrue(receiveChatMessage.ParseBody(buffer), L"Valid data can't be parsed"); - - Assert::AreEqual(authorUsername, receiveChatMessage.GetAuthorUsername(), L"Author username is wrong"); - Assert::AreEqual(chatMessage, receiveChatMessage.GetChatMessage(), L"Chat message is wrong"); - } - - TEST_METHOD(ParseSendChatMessage) - { - std::string validChatMessage = "Message"; - - SocketMessages::SendChatMessage sendChatMessage{}; - sendChatMessage.SetChatMessage(validChatMessage); - - std::vector messageBuffer = sendChatMessage.Serialize(); - std::vector buffer(messageBuffer.begin() + SocketMessages::MessageHeader::size, messageBuffer.end()); - - Assert::IsFalse(sendChatMessage.ParseBody({ 0, 3, 4 }), L"Invalid data can be parsed"); - Assert::IsTrue(sendChatMessage.ParseBody(buffer), L"Valid data can't be parsed"); - - Assert::AreEqual(validChatMessage, sendChatMessage.GetChatMessage(), L"Chat message is wrong"); - } - }; -} diff --git a/tests/ServerUnitTest/SerializeMessageUnitTests.cpp b/tests/ServerUnitTest/SerializeMessageUnitTests.cpp deleted file mode 100644 index 200bb2e..0000000 --- a/tests/ServerUnitTest/SerializeMessageUnitTests.cpp +++ /dev/null @@ -1,180 +0,0 @@ -#include "CppUnitTest.h" - -#include -#include - -#include "SocketMessages.h" - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -namespace ServerUnitTest -{ - TEST_CLASS(SerializeMessages) - { - public: - - TEST_METHOD(SerializeHeader) - { - SocketMessages::MessageHeader header; - header.messageType = SocketMessages::MessageType::AcknowledgeMessage; - header.bodySize = SocketMessages::AcknowledgeMessage().GetBodySize(); - header.messageId = Snowflake(); - std::vector buffer = header.Serialize(); - - uint64_t actualId = header.messageId; - uint64_t expectedId = *std::bit_cast(&buffer[sizeof(header.messageType) + sizeof(header.bodySize)]); - - Assert::AreEqual(buffer.size(), SocketMessages::MessageHeader::size, L"Wrong header size"); - Assert::AreEqual(buffer[0], static_cast(SocketMessages::MessageType::AcknowledgeMessage), L"Wrong header type"); - Assert::AreEqual(*std::bit_cast(&buffer[1]), header.bodySize, L"Wrong message type"); - Assert::AreEqual(expectedId, actualId, L"Wrong message content"); - } - - TEST_METHOD(SerializeACKMessage) - { - Snowflake acknowledgedMessageId; - - SocketMessages::AcknowledgeMessage message{ acknowledgedMessageId }; - std::vector buffer = message.Serialize(); - - uint64_t expectedSize = SocketMessages::MessageHeader::size + message.GetBodySize(); - uint64_t actualSize = buffer.size(); - - uint64_t expectedId = acknowledgedMessageId; - uint64_t actualId = *std::bit_cast(&buffer[SocketMessages::MessageHeader::size]); - - Assert::AreEqual(expectedSize, actualSize, L"Wrong message size"); - Assert::AreEqual(expectedId, actualId, L"Wrong Id"); - } - - TEST_METHOD(SerializeErrorMessage) - { - auto errorCode = SocketMessages::ErrorCode::InvalidMessage; - - SocketMessages::ErrorMessage message{ errorCode }; - std::vector buffer = message.Serialize(); - - uint64_t expectedSize = SocketMessages::MessageHeader::size + message.GetBodySize(); - uint64_t actualSize = buffer.size(); - - auto expectedError = static_cast(errorCode); - uint8_t actualError = *std::bit_cast(&buffer[SocketMessages::MessageHeader::size]); - - Assert::AreEqual(expectedSize, actualSize, L"Wrong message size"); - Assert::AreEqual(expectedError, actualError, L"Wrong error"); - } - - TEST_METHOD(SerializeHelloMessage) - { - SocketMessages::HelloMessage message{}; - std::vector buffer = message.Serialize(); - - uint64_t expectedVersion = CurrentVersion; - uint64_t actualVersion = *std::bit_cast(&buffer[SocketMessages::MessageHeader::size]); - - uint64_t expectedSize = SocketMessages::MessageHeader::size + message.GetBodySize(); - uint64_t actualSize = buffer.size(); - - Assert::AreEqual(expectedSize, actualSize, L"Wrong message size"); - Assert::AreEqual(expectedVersion, actualVersion, L"Wrong version"); - } - - TEST_METHOD(SerializeIdentifyMessage) - { - SocketMessages::IdentifyMessage message{}; - - std::string validUsername = "Username"; - std::string invalidUsername1 = "0"; - std::string invalidUsername2 = "012345678901234567890123456789012"; - - Assert::IsTrue(message.SetUsername(validUsername), L"Valid username can't be set"); - Assert::IsFalse(message.SetUsername(invalidUsername1), L"Short username can be set"); - Assert::IsFalse(message.SetUsername(invalidUsername2), L"Long username can be set"); - - std::vector buffer = message.Serialize(); - - uint64_t expectedSize = SocketMessages::MessageHeader::size + message.GetBodySize(); - uint64_t actualSize = buffer.size(); - - Assert::AreEqual(expectedSize, actualSize, L"Wrong message size"); - } - - TEST_METHOD(SerializeKeepAliveMessage) - { - SocketMessages::KeepAliveMessage message{}; - std::vector buffer = message.Serialize(); - - uint64_t expectedSize = SocketMessages::MessageHeader::size + message.GetBodySize(); - uint64_t actualSize = buffer.size(); - - Assert::AreEqual(expectedSize, actualSize, L"Wrong message size"); - } - - TEST_METHOD(SerializeReceiveChatMessage) - { - SocketMessages::ReceiveChatMessage message{}; - - std::string validUsername = "Username"; - std::string invalidUsername1 = "0"; - std::string invalidUsername2 = "012345678901234567890123456789012"; - - Assert::IsTrue(message.SetAuthorUsername(validUsername), L"Valid username can't be set"); - Assert::IsFalse(message.SetAuthorUsername(invalidUsername1), L"Short username can be set"); - Assert::IsFalse(message.SetAuthorUsername(invalidUsername2), L"Long username can be set"); - - std::string validMessage = "Message"; - std::string invalidMessage1 = ""; - std::string invalidMessage2(2001, 'a'); - - Assert::IsTrue(message.SetChatMessage(validMessage), L"Valid chat message can't be set"); - Assert::IsFalse(message.SetChatMessage(invalidMessage1), L"Short chat message can be set"); - Assert::IsFalse(message.SetChatMessage(invalidMessage2), L"Long username can be set"); - - std::vector buffer = message.Serialize(); - - uint64_t expectedSize = SocketMessages::MessageHeader::size + message.GetBodySize(); - uint64_t actualSize = buffer.size(); - - Assert::AreEqual(expectedSize, actualSize, L"Wrong message size"); - - uint64_t expectedMessageSize = message.GetChatMessage().size(); - uint64_t actualMessageSize = *std::bit_cast( - &buffer[SocketMessages::MessageHeader::size + sizeof(uint8_t) + validUsername.length()] - ); - - Assert::AreEqual(expectedMessageSize, actualMessageSize, L"Wrong chat message length"); - - auto expectedAuthorSize = static_cast(validUsername.size()); - uint8_t actualAuthorSize = *std::bit_cast(&buffer[SocketMessages::MessageHeader::size]); - - Assert::AreEqual(expectedAuthorSize, actualAuthorSize, L"Wrong author username length"); - } - - TEST_METHOD(SerializeSendChatMessage) - { - SocketMessages::SendChatMessage message{}; - - std::string validMessage = "Message"; - std::string invalidMessage1 = ""; - std::string invalidMessage2(2001, 'a'); - - Assert::IsTrue(message.SetChatMessage(validMessage), L"Valid chat message can't be set"); - Assert::IsFalse(message.SetChatMessage(invalidMessage1), L"Short chat message can be set"); - Assert::IsFalse(message.SetChatMessage(invalidMessage2), L"Long username can be set"); - - std::vector buffer = message.Serialize(); - - uint64_t expectedSize = SocketMessages::MessageHeader::size + message.GetBodySize(); - uint64_t actualSize = buffer.size(); - - Assert::AreEqual(expectedSize, actualSize, L"Wrong message size"); - - uint64_t expectedMessageSize = message.GetChatMessage().size(); - uint64_t actualMessageSize = *std::bit_cast( - &buffer[SocketMessages::MessageHeader::size] - ); - - Assert::AreEqual(expectedMessageSize, actualMessageSize, L"Wrong chat message length"); - } - }; -} diff --git a/tests/ServerUnitTest/ServerUnitTest.vcxproj b/tests/ServerUnitTest/ServerUnitTest.vcxproj deleted file mode 100644 index 74d009b..0000000 --- a/tests/ServerUnitTest/ServerUnitTest.vcxproj +++ /dev/null @@ -1,185 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 17.0 - {B53D4CAB-38CB-4597-AF97-D37FCCDD5D14} - Win32Proj - ServerUnitTest - 10.0 - NativeUnitTestProject - - - - DynamicLibrary - true - v143 - Unicode - false - - - DynamicLibrary - false - v143 - true - Unicode - false - - - DynamicLibrary - true - v143 - Unicode - false - - - DynamicLibrary - false - v143 - true - Unicode - false - - - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - NotUsing - Level3 - true - $(SolutionDir)Server\;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - _DEBUG;%(PreprocessorDefinitions) - true - pch.h - stdcpplatest - - - Windows - $(SolutionDir)Server\x64\Debug;$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - LinkVerbose - Connection.obj;Coroutine.obj;Coroutine.h.obj;Message.obj;Server.obj;ServerException.h.obj;ServerOnMessage.obj;User.obj;%(AdditionalDependencies) - - - - - NotUsing - Level3 - true - $(SolutionDir)Server\;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;%(PreprocessorDefinitions) - true - pch.h - stdcpplatest - - - Windows - $(SolutionDir)Server\x64\Debug;$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - LinkVerbose - Connection.obj;Coroutine.obj;Coroutine.h.obj;Message.obj;Server.obj;ServerException.h.obj;ServerOnMessage.obj;User.obj;%(AdditionalDependencies) - - - - - NotUsing - Level3 - true - true - true - $(SolutionDir)Server\;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;%(PreprocessorDefinitions) - true - pch.h - stdcpplatest - - - Windows - true - true - $(SolutionDir)Server\x64\Debug;$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - LinkVerbose - Connection.obj;Coroutine.obj;Coroutine.h.obj;Message.obj;Server.obj;ServerException.h.obj;ServerOnMessage.obj;User.obj;%(AdditionalDependencies) - - - - - NotUsing - Level3 - true - true - true - $(SolutionDir)Server\;$(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) - NDEBUG;%(PreprocessorDefinitions) - true - pch.h - stdcpplatest - - - Windows - true - true - $(SolutionDir)Server\x64\Debug;$(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - LinkVerbose - Connection.obj;Coroutine.obj;Coroutine.h.obj;Message.obj;Server.obj;ServerException.h.obj;ServerOnMessage.obj;User.obj;%(AdditionalDependencies) - - - - - - - - - {f11a6291-405c-4a94-ab56-f0027fdd4b4d} - - - {ffa917e0-c449-48c7-bdf3-9877cbd96b46} - - - - - - \ No newline at end of file diff --git a/tests/ServerUnitTest/ServerUnitTest.vcxproj.filters b/tests/ServerUnitTest/ServerUnitTest.vcxproj.filters deleted file mode 100644 index 6a76d6d..0000000 --- a/tests/ServerUnitTest/ServerUnitTest.vcxproj.filters +++ /dev/null @@ -1,25 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - \ No newline at end of file From a2ca2c4185f0334bc7fd36592d12e93e8cf03f25 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 18:31:31 +0200 Subject: [PATCH 084/203] Add hello message tests --- .../hello_message_tests.cpp | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/unit/socket_messages_tests/hello_message_tests.cpp diff --git a/tests/unit/socket_messages_tests/hello_message_tests.cpp b/tests/unit/socket_messages_tests/hello_message_tests.cpp new file mode 100644 index 0000000..2599cb1 --- /dev/null +++ b/tests/unit/socket_messages_tests/hello_message_tests.cpp @@ -0,0 +1,53 @@ +#include + +#include +#include + +#include +#include + +TEST(hello_message, default_constructor) +{ + pine::socket_messages::hello_message msg; + EXPECT_EQ(msg.header.type, pine::socket_messages::message_type::HELLO_MESSAGE); + EXPECT_EQ(msg.header.body_size, sizeof(uint64_t)); +} + +TEST(hello_message, parse_body) +{ + pine::socket_messages::hello_message msg; + + std::vector body(0, 0); + EXPECT_FALSE(msg.parse_body(body)); + + body.resize(sizeof(uint64_t), 0); + body[0] = 0x01; + EXPECT_TRUE(msg.parse_body(body)); + + EXPECT_EQ(msg.m_version, 0x01); +} + +TEST(hello_message, serialize) +{ + pine::socket_messages::hello_message msg; + msg.m_version = 0x01; + + std::vector buffer = msg.serialize(); + + EXPECT_EQ(buffer.size(), pine::socket_messages::message_header::size + sizeof(uint64_t)); + + pine::socket_messages::message_header header; + header.parse(buffer); + EXPECT_EQ(header.type, pine::socket_messages::message_type::HELLO_MESSAGE); + EXPECT_EQ(header.body_size, sizeof(uint64_t)); + + uint64_t version{}; + std::copy_n(buffer.begin() + pine::socket_messages::message_header::size, sizeof(version), reinterpret_cast(&version)); + EXPECT_EQ(version, 0x01); +} + +TEST(hello_message, get_body_size) +{ + pine::socket_messages::hello_message msg; + EXPECT_EQ(msg.get_body_size(), sizeof(uint64_t)); +} \ No newline at end of file From dd6fb42a975d6be0076dc508213b9aeb7137f539 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 18:33:51 +0200 Subject: [PATCH 085/203] Make check_username const and use .size() instead of .length() --- shared/include/socket_messages/identify_message.h | 2 +- shared/src/socket_messages/identify_message.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/include/socket_messages/identify_message.h b/shared/include/socket_messages/identify_message.h index b9c2051..d855b1e 100644 --- a/shared/include/socket_messages/identify_message.h +++ b/shared/include/socket_messages/identify_message.h @@ -19,7 +19,7 @@ namespace pine::socket_messages uint64_t get_body_size() const final; - constexpr bool check_username(std::string_view const& username); + constexpr bool check_username(std::string_view const& username) const; std::string username{}; }; diff --git a/shared/src/socket_messages/identify_message.cpp b/shared/src/socket_messages/identify_message.cpp index 65fe19a..2050491 100644 --- a/shared/src/socket_messages/identify_message.cpp +++ b/shared/src/socket_messages/identify_message.cpp @@ -49,10 +49,10 @@ namespace pine::socket_messages size_t identify_message::get_body_size() const { - return sizeof(uint8_t) + username.length(); + return sizeof(uint8_t) + username.size(); } - constexpr bool identify_message::check_username(std::string_view const& name) + constexpr bool identify_message::check_username(std::string_view const& name) const { if (username.length() < username_min_length || username.length() > username_max_length) return false; From ff2fa78fde2463cdf455f2ab3f93b77289d1b59f Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 18:50:06 +0200 Subject: [PATCH 086/203] Write tests for identify message --- .../identify_message_tests.cpp | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tests/unit/socket_messages_tests/identify_message_tests.cpp diff --git a/tests/unit/socket_messages_tests/identify_message_tests.cpp b/tests/unit/socket_messages_tests/identify_message_tests.cpp new file mode 100644 index 0000000..1bf71f1 --- /dev/null +++ b/tests/unit/socket_messages_tests/identify_message_tests.cpp @@ -0,0 +1,77 @@ +#include + +#include +#include +#include + +#include +#include + +TEST(identify_message, default_constructor) +{ + pine::socket_messages::identify_message msg; + EXPECT_EQ(msg.header.type, pine::socket_messages::message_type::IDENTIFY_MESSAGE); + EXPECT_EQ(msg.header.body_size, sizeof(uint8_t)); +} + +TEST(identify_message, construct_from_name) +{ + std::string name = "test"; + pine::socket_messages::identify_message msg(name); + + EXPECT_EQ(msg.header.type, pine::socket_messages::message_type::IDENTIFY_MESSAGE); + EXPECT_EQ(msg.header.body_size, sizeof(uint8_t) + name.size()); + EXPECT_EQ(msg.username, name); +} + +TEST(identify_message, parse_body) +{ + pine::socket_messages::identify_message msg; + std::string name = "test"; + + std::vector body(0, 0); + EXPECT_FALSE(msg.parse_body(body)); + + body.resize(sizeof(uint8_t) + name.size(), 0); + body[0] = name.size(); + std::copy_n(name.begin(), name.size(), body.begin() + sizeof(uint8_t)); + EXPECT_TRUE(msg.parse_body(body)); + + EXPECT_EQ(msg.username, name); +} + +TEST(identify_message, serialize) +{ + pine::socket_messages::identify_message msg; + msg.username = "test"; + + std::vector buffer = msg.serialize(); + + EXPECT_EQ(buffer.size(), pine::socket_messages::message_header::size + sizeof(uint8_t) + msg.username.size()); + + pine::socket_messages::message_header header; + header.parse(buffer); + EXPECT_EQ(header.type, pine::socket_messages::message_type::IDENTIFY_MESSAGE); + EXPECT_EQ(header.body_size, sizeof(uint8_t) + msg.username.size()); + + uint8_t username_size{}; + std::copy_n(buffer.begin() + pine::socket_messages::message_header::size, sizeof(username_size), &username_size); + EXPECT_EQ(username_size, msg.username.size()); + + std::string username(buffer.begin() + pine::socket_messages::message_header::size + sizeof(username_size), buffer.end()); + EXPECT_EQ(username, msg.username); +} + +TEST(identify_message, get_body_size) +{ + pine::socket_messages::identify_message msg; + msg.username = "test"; + EXPECT_EQ(msg.get_body_size(), sizeof(uint8_t) + msg.username.size()); +} + +TEST(identify_message, check_username) +{ + EXPECT_TRUE(pine::socket_messages::identify_message::check_username("test")); + EXPECT_FALSE(pine::socket_messages::identify_message::check_username("")); + EXPECT_FALSE(pine::socket_messages::identify_message::check_username("0102030405060708091011121314151617")); +} \ No newline at end of file From b53882346eea980d48aef58919fcf4a1543b4b8c Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 18:50:20 +0200 Subject: [PATCH 087/203] Fix link error --- shared/include/socket_messages/identify_message.h | 8 +++++++- shared/src/socket_messages/identify_message.cpp | 8 -------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/shared/include/socket_messages/identify_message.h b/shared/include/socket_messages/identify_message.h index d855b1e..5952d72 100644 --- a/shared/include/socket_messages/identify_message.h +++ b/shared/include/socket_messages/identify_message.h @@ -19,7 +19,13 @@ namespace pine::socket_messages uint64_t get_body_size() const final; - constexpr bool check_username(std::string_view const& username) const; + static constexpr bool check_username(std::string_view const& name) + { + if (name.length() < username_min_length || name.length() > username_max_length) + return false; + + return true; + } std::string username{}; }; diff --git a/shared/src/socket_messages/identify_message.cpp b/shared/src/socket_messages/identify_message.cpp index 2050491..5a083f9 100644 --- a/shared/src/socket_messages/identify_message.cpp +++ b/shared/src/socket_messages/identify_message.cpp @@ -51,12 +51,4 @@ namespace pine::socket_messages { return sizeof(uint8_t) + username.size(); } - - constexpr bool identify_message::check_username(std::string_view const& name) const - { - if (username.length() < username_min_length || username.length() > username_max_length) - return false; - - return true; - } } \ No newline at end of file From d215f4eb0d183b14a7a39b6f70cb504007b2068e Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 18:53:17 +0200 Subject: [PATCH 088/203] Fix test, header size not computed with name assignment --- tests/unit/socket_messages_tests/identify_message_tests.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/unit/socket_messages_tests/identify_message_tests.cpp b/tests/unit/socket_messages_tests/identify_message_tests.cpp index 1bf71f1..0635b55 100644 --- a/tests/unit/socket_messages_tests/identify_message_tests.cpp +++ b/tests/unit/socket_messages_tests/identify_message_tests.cpp @@ -42,8 +42,7 @@ TEST(identify_message, parse_body) TEST(identify_message, serialize) { - pine::socket_messages::identify_message msg; - msg.username = "test"; + pine::socket_messages::identify_message msg("test"); std::vector buffer = msg.serialize(); From 9570f1b5e6fe02f2a7352598a8ff27ca4695346b Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 18:55:32 +0200 Subject: [PATCH 089/203] Add keep alive message test --- .../keep_alive_message_tests.cpp | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/unit/socket_messages_tests/keep_alive_message_tests.cpp diff --git a/tests/unit/socket_messages_tests/keep_alive_message_tests.cpp b/tests/unit/socket_messages_tests/keep_alive_message_tests.cpp new file mode 100644 index 0000000..fb50994 --- /dev/null +++ b/tests/unit/socket_messages_tests/keep_alive_message_tests.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include + +#include +#include + +TEST(keep_alive_message_tests, constructor) +{ + pine::socket_messages::keep_alive_message message{}; + + EXPECT_EQ(message.header.type, pine::socket_messages::message_type::KEEP_ALIVE_MESSAGE); + EXPECT_EQ(message.header.body_size, 0); +} + +TEST(keep_alive_message_tests, parse_body) +{ + pine::socket_messages::keep_alive_message message{}; + + std::vector buffer(0, 0); + EXPECT_TRUE(message.parse_body(buffer)); + + buffer.resize(1, 0); + EXPECT_FALSE(message.parse_body(buffer)); +} + +TEST(keep_alive_message_tests, serialize) +{ + pine::socket_messages::keep_alive_message message{}; + + std::vector buffer = message.serialize(); + + EXPECT_EQ(buffer.size(), pine::socket_messages::message_header::size); + + pine::socket_messages::message_header header; + header.parse(buffer); + EXPECT_EQ(header.type, pine::socket_messages::message_type::KEEP_ALIVE_MESSAGE); + EXPECT_EQ(header.body_size, 0); +} + +TEST(keep_alive_message_tests, get_body_size) +{ + pine::socket_messages::keep_alive_message message{}; + EXPECT_EQ(message.get_body_size(), 0); +} From 9b70f428b1b2f7b27e5c76cd33ca9bd26daf677c Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 20:02:26 +0200 Subject: [PATCH 090/203] Add tests for receive chat message --- .../receive_chat_message_tests.cpp | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 tests/unit/socket_messages_tests/receive_chat_message_tests.cpp diff --git a/tests/unit/socket_messages_tests/receive_chat_message_tests.cpp b/tests/unit/socket_messages_tests/receive_chat_message_tests.cpp new file mode 100644 index 0000000..bc36e1e --- /dev/null +++ b/tests/unit/socket_messages_tests/receive_chat_message_tests.cpp @@ -0,0 +1,140 @@ +#include + +#include +#include +#include + +#include +#include + +TEST(receive_chat_message, default_constructor) +{ + pine::socket_messages::receive_chat_message message; + + EXPECT_EQ(message.header.type, pine::socket_messages::message_type::RECEIVE_CHAT_MESSAGE); + EXPECT_EQ(message.header.body_size, sizeof(uint8_t) + sizeof(uint16_t)); + EXPECT_EQ(message.author_username, ""); + EXPECT_EQ(message.message_content, ""); +} + +TEST(receive_chat_message, parse_body) +{ + pine::socket_messages::receive_chat_message message; + + // Invalid buffer size : too small + std::vector buffer(0, 0); + EXPECT_FALSE(message.parse_body(buffer)); + + // Invalid buffer size : too big + buffer.resize(sizeof(uint8_t) + pine::username_max_length + sizeof(uint16_t) + pine::chat_message_max_length + 1); + EXPECT_FALSE(message.parse_body(buffer)); + + // Invalid username length : too small + buffer.resize(sizeof(uint8_t) + pine::username_min_length + sizeof(uint16_t) + pine::chat_message_min_length); + buffer[0] = pine::username_min_length - 1; + EXPECT_FALSE(message.parse_body(buffer)); + + // Invalid username length : too big + buffer[0] = pine::username_max_length + 1; + EXPECT_FALSE(message.parse_body(buffer)); + + // Invalid message length : too small + buffer[0] = pine::username_min_length; + *reinterpret_cast(buffer.data() + sizeof(uint8_t) + pine::username_min_length) = + pine::chat_message_min_length - 1; + EXPECT_FALSE(message.parse_body(buffer)); + + // Invalid message length : too big + *reinterpret_cast(buffer.data() + sizeof(uint8_t) + pine::username_min_length) = + pine::chat_message_max_length + 1; + EXPECT_FALSE(message.parse_body(buffer)); + + // Invalid buffer size + *reinterpret_cast(buffer.data() + sizeof(uint8_t) + pine::username_min_length) = + pine::chat_message_min_length; + buffer.resize(sizeof(uint8_t) + pine::username_min_length + sizeof(uint16_t) + pine::chat_message_min_length + 1); + EXPECT_FALSE(message.parse_body(buffer)); + + // Valid buffer + std::string_view username = "username"; + std::string_view message_content = "message"; + buffer.resize(sizeof(uint8_t) + username.size() + sizeof(uint16_t) + message_content.size()); + buffer[0] = username.size(); + std::copy_n(username.begin(), username.size(), buffer.begin() + sizeof(uint8_t)); + *reinterpret_cast(buffer.data() + sizeof(uint8_t) + username.size()) = message_content.size(); + std::copy_n(message_content.begin(), message_content.size(), buffer.begin() + sizeof(uint8_t) + username.size() + sizeof(uint16_t)); + EXPECT_TRUE(message.parse_body(buffer)); +} + +TEST(receive_chat_message, serialize) +{ + pine::socket_messages::receive_chat_message msg; + msg.author_username = "username"; + msg.message_content = "message"; + msg.header.body_size = msg.get_body_size(); + + std::vector buffer = msg.serialize(); + + EXPECT_EQ(buffer.size(), pine::socket_messages::message_header::size + msg.get_body_size()); + EXPECT_EQ(buffer[0], msg.header.type); + EXPECT_EQ(buffer[1], msg.header.body_size); + EXPECT_EQ(buffer[pine::socket_messages::message_header::size], msg.author_username.size()); + + auto it = buffer.begin() + pine::socket_messages::message_header::size + sizeof(uint8_t); + EXPECT_TRUE(std::equal(msg.author_username.begin(), msg.author_username.end(), it)); + + it += msg.author_username.size(); + EXPECT_EQ(*reinterpret_cast(&*it), msg.message_content.size()); + + it += sizeof(uint16_t); + EXPECT_TRUE(std::equal(msg.message_content.begin(), msg.message_content.end(), it)); + + it += msg.message_content.size(); + EXPECT_EQ(it, buffer.end()); +} + +TEST(receive_chat_message, get_body_size) +{ + pine::socket_messages::receive_chat_message msg; + + EXPECT_EQ(msg.get_body_size(), sizeof(uint8_t) + sizeof(uint16_t)); + + msg.author_username = "username"; + msg.message_content = "message"; + + EXPECT_EQ(msg.get_body_size(), sizeof(uint8_t) + msg.author_username.size() + sizeof(uint16_t) + msg.message_content.size()); +} + +TEST(receive_chat_message, check_author_username) +{ + EXPECT_FALSE(pine::socket_messages::receive_chat_message::check_author_username("")); + + std::string username(pine::username_min_length - 1, 'a'); + EXPECT_FALSE(pine::socket_messages::receive_chat_message::check_author_username(username)); + + username.resize(pine::username_max_length + 1); + EXPECT_FALSE(pine::socket_messages::receive_chat_message::check_author_username(username)); + + username.resize(pine::username_min_length); + EXPECT_TRUE(pine::socket_messages::receive_chat_message::check_author_username(username)); + + username.resize(pine::username_max_length); + EXPECT_TRUE(pine::socket_messages::receive_chat_message::check_author_username(username)); +} + +TEST(receive_chat_message, check_message_content) +{ + EXPECT_FALSE(pine::socket_messages::receive_chat_message::check_message_content("")); + + std::string message(pine::chat_message_min_length - 1, 'a'); + EXPECT_FALSE(pine::socket_messages::receive_chat_message::check_message_content(message)); + + message.resize(pine::chat_message_max_length + 1); + EXPECT_FALSE(pine::socket_messages::receive_chat_message::check_message_content(message)); + + message.resize(pine::chat_message_min_length); + EXPECT_TRUE(pine::socket_messages::receive_chat_message::check_message_content(message)); + + message.resize(pine::chat_message_max_length); + EXPECT_TRUE(pine::socket_messages::receive_chat_message::check_message_content(message)); +} \ No newline at end of file From a0949ded57a9e884f51f95e10ee09b89f1c314bf Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 20:03:04 +0200 Subject: [PATCH 091/203] Add checks for username lenght --- shared/src/socket_messages/receive_chat_message.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shared/src/socket_messages/receive_chat_message.cpp b/shared/src/socket_messages/receive_chat_message.cpp index 2868765..c955753 100644 --- a/shared/src/socket_messages/receive_chat_message.cpp +++ b/shared/src/socket_messages/receive_chat_message.cpp @@ -21,12 +21,16 @@ namespace pine::socket_messages return false; uint8_t username_length = buffer[0]; + if (username_length < username_min_length || username_length > username_max_length) + return false; if (buffer.size() < sizeof(uint8_t) + username_length + sizeof(uint16_t) + chat_message_min_length) return false; author_username = std::string(buffer.begin() + sizeof(uint8_t), buffer.begin() + sizeof(uint8_t) + username_length); uint16_t message_length = *reinterpret_cast(buffer.data() + sizeof(uint8_t) + username_length); + if (message_length < chat_message_min_length || message_length > chat_message_max_length) + return false; if (buffer.size() != sizeof(uint8_t) + username_length + sizeof(uint16_t) + message_length) return false; From 6eef234ffcd48d7e1fc1bb639a583df9d7919993 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 20:03:27 +0200 Subject: [PATCH 092/203] Make receive message checks static --- .../socket_messages/receive_chat_message.h | 23 +++++++++++++++-- .../socket_messages/receive_chat_message.cpp | 25 +++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/shared/include/socket_messages/receive_chat_message.h b/shared/include/socket_messages/receive_chat_message.h index 19e7dcb..aed1eae 100644 --- a/shared/include/socket_messages/receive_chat_message.h +++ b/shared/include/socket_messages/receive_chat_message.h @@ -18,8 +18,27 @@ namespace pine::socket_messages uint64_t get_body_size() const final; - constexpr bool check_author_username(std::string_view const& author_username); - constexpr bool check_message_content(std::string_view const& message_content); + static constexpr bool check_author_username(std::string_view const& username) + { + if (username.size() < username_min_length) + return false; + + if (username.size() > username_max_length) + return false; + + return true; + } + + static constexpr bool check_message_content(std::string_view const& content) + { + if (content.size() < chat_message_min_length) + return false; + + if (content.size() > chat_message_max_length) + return false; + + return true; + } std::string author_username{}; std::string message_content{}; diff --git a/shared/src/socket_messages/receive_chat_message.cpp b/shared/src/socket_messages/receive_chat_message.cpp index c955753..0a5e92f 100644 --- a/shared/src/socket_messages/receive_chat_message.cpp +++ b/shared/src/socket_messages/receive_chat_message.cpp @@ -1,9 +1,12 @@ #include "socket_messages/receive_chat_message.h" #include +#include #include #include +#include "message.h" + namespace pine::socket_messages { receive_chat_message::receive_chat_message() @@ -61,26 +64,4 @@ namespace pine::socket_messages { return sizeof(uint8_t) + author_username.size() + sizeof(uint16_t) + message_content.size(); } - - constexpr bool receive_chat_message::check_author_username(std::string_view const& username) - { - if (username.size() < username_min_length) - return false; - - if (username.size() > username_max_length) - return false; - - return true; - } - - constexpr bool receive_chat_message::check_message_content(std::string_view const& content) - { - if (content.size() < chat_message_min_length) - return false; - - if (content.size() > chat_message_max_length) - return false; - - return true; - } } \ No newline at end of file From a133521f72940357be62412acd4f05c3e0045e27 Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 20:22:24 +0200 Subject: [PATCH 093/203] Change check message content from send chat message to static --- shared/include/socket_messages/send_chat_message.h | 8 +++++++- shared/src/socket_messages/send_chat_message.cpp | 8 -------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/shared/include/socket_messages/send_chat_message.h b/shared/include/socket_messages/send_chat_message.h index decce17..4fe4b47 100644 --- a/shared/include/socket_messages/send_chat_message.h +++ b/shared/include/socket_messages/send_chat_message.h @@ -18,7 +18,13 @@ namespace pine::socket_messages uint64_t constexpr get_body_size() const final; - constexpr bool check_message_content(std::string_view const& content); + static constexpr bool check_message_content(std::string_view const& content) + { + if (content.size() < chat_message_min_length || content.size() > chat_message_max_length) + return false; + + return true; + } std::string message_content{}; }; diff --git a/shared/src/socket_messages/send_chat_message.cpp b/shared/src/socket_messages/send_chat_message.cpp index 8c0e9c5..11b4f25 100644 --- a/shared/src/socket_messages/send_chat_message.cpp +++ b/shared/src/socket_messages/send_chat_message.cpp @@ -48,12 +48,4 @@ namespace pine::socket_messages { return sizeof(uint16_t) + message_content.size(); } - - constexpr bool send_chat_message::check_message_content(std::string_view const& content) - { - if (content.size() < chat_message_min_length || content.size() > chat_message_max_length) - return false; - - return true; - } } \ No newline at end of file From 306a7e4585a4f56a06a010a52009e40eb199eabe Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 20:22:40 +0200 Subject: [PATCH 094/203] Add check for message length --- shared/src/socket_messages/send_chat_message.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/src/socket_messages/send_chat_message.cpp b/shared/src/socket_messages/send_chat_message.cpp index 11b4f25..2b4cbbd 100644 --- a/shared/src/socket_messages/send_chat_message.cpp +++ b/shared/src/socket_messages/send_chat_message.cpp @@ -21,6 +21,8 @@ namespace pine::socket_messages return false; uint16_t message_length = *reinterpret_cast(buffer.data()); + if (message_length < chat_message_min_length || message_length > chat_message_max_length) + return false; if (buffer.size() != sizeof(uint16_t) + message_length) return false; From 046a4281c5f849c8194b3b1e69b2ee0902458aaa Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 20:22:50 +0200 Subject: [PATCH 095/203] Add tests for send message --- .../send_message_tests.cpp | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 tests/unit/socket_messages_tests/send_message_tests.cpp diff --git a/tests/unit/socket_messages_tests/send_message_tests.cpp b/tests/unit/socket_messages_tests/send_message_tests.cpp new file mode 100644 index 0000000..79a0445 --- /dev/null +++ b/tests/unit/socket_messages_tests/send_message_tests.cpp @@ -0,0 +1,84 @@ +#include + +#include +#include +#include + +#include +#include + +TEST(send_chat_message, default_constructor) +{ + pine::socket_messages::send_chat_message message; + EXPECT_EQ(message.header.type, pine::socket_messages::message_type::SEND_CHAT_MESSAGE); + EXPECT_EQ(message.header.body_size, sizeof(uint16_t)); + EXPECT_EQ(message.message_content, ""); +} + +TEST(send_chat_message, parse_body) +{ + pine::socket_messages::send_chat_message message; + + // Invalid buffer size : too small + std::vector buffer(0, 0); + EXPECT_FALSE(message.parse_body(buffer)); + + // Invalid buffer size : too big + buffer.resize(sizeof(uint16_t) + pine::chat_message_max_length + 1); + EXPECT_FALSE(message.parse_body(buffer)); + + // Invalid message length : too small + buffer.resize(sizeof(uint16_t) + pine::chat_message_min_length); + *reinterpret_cast(buffer.data()) = pine::chat_message_min_length - 1; + EXPECT_FALSE(message.parse_body(buffer)); + + // Invalid message length : too big + *reinterpret_cast(buffer.data()) = pine::chat_message_max_length + 1; + EXPECT_FALSE(message.parse_body(buffer)); + + // Invalid buffer size + *reinterpret_cast(buffer.data()) = pine::chat_message_max_length; + EXPECT_FALSE(message.parse_body(buffer)); + + // Valid buffer + std::string_view message_content = "message"; + *reinterpret_cast(buffer.data()) = static_cast(message_content.size()); + buffer.resize(sizeof(uint16_t) + message_content.size()); + std::copy_n(message_content.begin(), message_content.size(), buffer.begin() + sizeof(uint16_t)); + EXPECT_TRUE(message.parse_body(buffer)); + EXPECT_EQ(message.message_content, message_content); +} + +TEST(send_chat_message, serialize) +{ + pine::socket_messages::send_chat_message message; + message.message_content = "message"; + message.header.body_size = message.get_body_size(); + + std::vector buffer = message.serialize(); + std::vector ::iterator it = buffer.begin(); + + EXPECT_EQ(buffer.size(), pine::socket_messages::message_header::size + message.get_body_size()); + EXPECT_EQ(buffer[0], static_cast(pine::socket_messages::message_type::SEND_CHAT_MESSAGE)); + it += sizeof(uint8_t); + EXPECT_EQ(*reinterpret_cast(&*it), message.header.body_size); + it += 2 * sizeof(uint64_t); + EXPECT_EQ(*reinterpret_cast(&*it), static_cast(message.message_content.size())); + it += sizeof(uint16_t); + EXPECT_EQ(std::string(it, buffer.end()), message.message_content); +} + +TEST(send_chat_message, get_body_size) +{ + pine::socket_messages::send_chat_message message; + message.message_content = "message"; + EXPECT_EQ(message.get_body_size(), sizeof(uint16_t) + message.message_content.size()); +} + +TEST(send_chat_message, check_message_content) +{ + EXPECT_FALSE(pine::socket_messages::send_chat_message::check_message_content("")); + EXPECT_FALSE(pine::socket_messages::send_chat_message::check_message_content(std::string(pine::chat_message_max_length + 1, 'a'))); + EXPECT_TRUE(pine::socket_messages::send_chat_message::check_message_content(std::string(pine::chat_message_max_length, 'a'))); + EXPECT_TRUE(pine::socket_messages::send_chat_message::check_message_content(std::string(pine::chat_message_min_length, 'a'))); +} \ No newline at end of file From bb667e2b794553cf67bb055e5c6409de2d8ff66b Mon Sep 17 00:00:00 2001 From: Jacques Date: Fri, 8 Sep 2023 21:55:22 +0200 Subject: [PATCH 096/203] Remove Visual Studio projects --- Pine/App.xaml | 16 -- Pine/App.xaml.cs | 56 ------- Pine/Assets/LockScreenLogo.scale-200.png | Bin 432 -> 0 bytes Pine/Assets/SplashScreen.scale-200.png | Bin 5372 -> 0 bytes Pine/Assets/Square150x150Logo.scale-200.png | Bin 1755 -> 0 bytes Pine/Assets/Square44x44Logo.scale-200.png | Bin 637 -> 0 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 283 -> 0 bytes Pine/Assets/StoreLogo.png | Bin 456 -> 0 bytes Pine/Assets/Wide310x150Logo.scale-200.png | Bin 2097 -> 0 bytes Pine/Client/PineClient.cs | 136 ---------------- Pine/Package.appxmanifest | 51 ------ Pine/Pine.csproj | 80 ---------- Pine/Properties/launchSettings.json | 10 -- Pine/SocketMessages/HelloMessage.cs | 42 ----- Pine/SocketMessages/LoginMessage.cs | 51 ------ Pine/SocketMessages/Message.cs | 82 ---------- Pine/Views/Identify/IdentifyControl.xaml | 15 -- Pine/Views/Identify/IdentifyControl.xaml.cs | 45 ------ Pine/Views/Identify/IdentifyPage.xaml | 18 --- Pine/Views/Identify/IdentifyPage.xaml.cs | 60 ------- Pine/Views/MainWindow.xaml | 13 -- Pine/Views/MainWindow.xaml.cs | 20 --- Pine/app.manifest | 20 --- PineServer/PineServer.vcxproj | 151 ------------------ PineServer/PineServer.vcxproj.filters | 22 --- PineServer/main.cpp | 7 - 26 files changed, 895 deletions(-) delete mode 100644 Pine/App.xaml delete mode 100644 Pine/App.xaml.cs delete mode 100644 Pine/Assets/LockScreenLogo.scale-200.png delete mode 100644 Pine/Assets/SplashScreen.scale-200.png delete mode 100644 Pine/Assets/Square150x150Logo.scale-200.png delete mode 100644 Pine/Assets/Square44x44Logo.scale-200.png delete mode 100644 Pine/Assets/Square44x44Logo.targetsize-24_altform-unplated.png delete mode 100644 Pine/Assets/StoreLogo.png delete mode 100644 Pine/Assets/Wide310x150Logo.scale-200.png delete mode 100644 Pine/Client/PineClient.cs delete mode 100644 Pine/Package.appxmanifest delete mode 100644 Pine/Pine.csproj delete mode 100644 Pine/Properties/launchSettings.json delete mode 100644 Pine/SocketMessages/HelloMessage.cs delete mode 100644 Pine/SocketMessages/LoginMessage.cs delete mode 100644 Pine/SocketMessages/Message.cs delete mode 100644 Pine/Views/Identify/IdentifyControl.xaml delete mode 100644 Pine/Views/Identify/IdentifyControl.xaml.cs delete mode 100644 Pine/Views/Identify/IdentifyPage.xaml delete mode 100644 Pine/Views/Identify/IdentifyPage.xaml.cs delete mode 100644 Pine/Views/MainWindow.xaml delete mode 100644 Pine/Views/MainWindow.xaml.cs delete mode 100644 Pine/app.manifest delete mode 100644 PineServer/PineServer.vcxproj delete mode 100644 PineServer/PineServer.vcxproj.filters delete mode 100644 PineServer/main.cpp diff --git a/Pine/App.xaml b/Pine/App.xaml deleted file mode 100644 index b5e8437..0000000 --- a/Pine/App.xaml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - diff --git a/Pine/App.xaml.cs b/Pine/App.xaml.cs deleted file mode 100644 index 7a06375..0000000 --- a/Pine/App.xaml.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; -using Microsoft.UI.Xaml.Shapes; -using Pine.Client; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.ApplicationModel; -using Windows.ApplicationModel.Activation; -using Windows.Foundation; -using Windows.Foundation.Collections; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace Pine -{ - /// - /// Provides application-specific behavior to supplement the default Application class. - /// - public partial class App : Application - { - /// - /// Initializes the singleton application object. This is the first line of authored code - /// executed, and as such is the logical equivalent of main() or WinMain(). - /// - public App() - { - InitializeComponent(); - - PineClient = new PineClient(); - _ = PineClient.ConnectAsync("127.0.0.1", 45321); - } - - /// - /// Invoked when the application is launched. - /// - /// Details about the launch request and process. - protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) - { - m_window = new MainWindow(); - m_window.Activate(); - } - - public static PineClient PineClient { get; private set; } - - private Window m_window; - } -} diff --git a/Pine/Assets/LockScreenLogo.scale-200.png b/Pine/Assets/LockScreenLogo.scale-200.png deleted file mode 100644 index 7440f0d4bf7c7e26e4e36328738c68e624ee851e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 432 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezr3(FqV6|IEGZ*x-#9g>~Mkr+x6^F zy~CDX2QIMs&Gcs3RnRBoxBA!*(Mfw0KTCYuYk0WlEIV>qBmPl! zq4ukrvfADX@#p8fbLY(H47N+k`FZ(FZh?cDro7>{8mkBO3>^oaIx`3!Jl)Qq)HI!+ z(S=1{o~eT)&W^=Ea8C`-17(Jv5(nHFJ{dOjGdxLVkY_y6&S1whfuFI4MM0kF0f&cO zPDVpV%nz;Id$>+0Ga5e9625-JcI)oq=#Pa3p^>8BB}21BUw@eN!-6@w%X+^`+Vn?! zryu|3T>kVWNBYyBc=7Y6H#s1Ah!OI_nezW zXTqOdkv2Az6KKBV=$yHdF^R3Fqw(TZEoNSZX>reXJ#bwX42%f|Pgg&ebxsLQ010xn AssI20 diff --git a/Pine/Assets/SplashScreen.scale-200.png b/Pine/Assets/SplashScreen.scale-200.png deleted file mode 100644 index 32f486a86792a5e34cd9a8261b394c49b48f86be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5372 zcmd5=Z){Ul6u)iv53sCbIJKLzl(EF%0tzcEY@|pLrfgF~2Dk$KFtU+$kbYqDN5W%7 z>?DBo!@y06eh{Oux>brrNT^{MO(tkiC@nH(2}}G_1|uvcMD(0{?|W^Gxo!tG~hW2Rn&7%b`-Kd_^`BCrb>XVtRKONoEw6%NswzMxk+kbocuk&}kJ#hSP z>8uR{r%LJ?I#)aaWW;uEixz+DzyTpp)MTEo&R%nEA92~g{^eXQwKV1m{xl5K<@k3FacT+Z zrwfy=VocIptI>t%@p5a;Rt=WXVnU;2SUdr7Yk>gw_2z_ICK^23$|Cg7{3Eg5j@N*F zetT?>30(*S_7ld-Yt&u7T{(hEjjM#vPlXibjrq?;pBBx3*>_2~VFGdsH5L zQKme_LAebV}aOX#+rQafZtp+4jK}V!>pn1?+eUH$0%6}z(Kul9!^2z zXi+d@jnx)RW7!j9uFEdv5N&1sCW#Z6Ej5Y7c;o28Q7i%U0(2v5J>o9P zl$#C8&9r)nL;?J65^GIeSOHYr3B7}}R~}@2Tx_xo5*YdU#g1bO}95cq69J!efdlE+xj1qG#ZUqh~1Sn#dBsZfDvcupM zXOFoyJ0$s+RHQKpzr#T>c&EUbq)lGvZDxuI!9unMI=#;ob2&gT)WqOjt6^X`_N21r`&eh6h0xpT!n6Z9rvE&+bFU$vTJO2? z#^tBNOx*2N)~(+TH8d>ep6``8V=3JEfdUUahVZ-xN+k#V&32x|%qnX(XBii5<@`%^ zV#Ky4f1!6RJqJXBU3M4~tmj2;;r`8_j&w?h5g35uMH(QI$Xpesb zG|*XRT?kh6M(jj0Y&vF^M*9g-iDMW%G%9%Pa}6ERQ9b0%6z1v}Ja=|L@G#5ZI>JS9 z*(K12nMvS?oyG8s9|q~{w`ajtI`KSHSiJ;)%X@M&eCE(VqI#F(XL?L@A$TUT?6av5 zkPWIR391XjSC%d6L}7F71Qpw(;c_~)mSZo-&Fm^FHlPX|Fu}1B3E+9j0}o1a(4HFS zUItE22CC%XZi!b4%~vWn>rpV9&CUEvt!?Q{Pr*L~51&(0Sz{VJJFrJtWw2PwXd|J{ zgH%3vAY$flodH=4&ruCHX;(3t;o}n?!0~3EE|5qRz$!VIkphxa4@_jyfiE9m;0 zjcYJ2;26N&MTB8X4joZ&?SUe|VS$^I%dt{!c2O;%3SdqW@K_14r8eyC1s&VcU5+2~ z_O1Cc*w|aIA=VC6AT_EFoL}W#Rl;7CZe)e}RS*e;8CVyM6i8a(yO@|S709VYY(y2g zc+QxB>Bw^B^2Db~*o)=i$m-aUNQFkYy5(eJW$cez>C{POds*p3cy#tHnvActP;dBP zdEf)C;lq}&#PE?XCD<~ngrzYUg|nS`#MS`Rd7cT>xlR19P#~4Qg5!J}@glCUq)z_2 zjvyv%aSq0 z)njao1dV0XNw&c@qmj1e*jgQ$l@_urW5G4RSY#rT1z`#%3;{EB`aJK|TH^lb_3nAT z-_Q4X-(K&IS8UyqsnjYdippfmN-HT!X2MT;Dpcy~-#$k6V z|MR4vU#O&p7TC46pTflb3 zoUJ;ZRf#&8&EwXy5s%!&(q6cN62swD#FH%O-RJsjWPZN3^^@FCIQ&MxXIFo7!I#VI zkpIstuWqUV5uhgs07?k$*!`uiZ=5b#$lI|0c+XJvj(}zSE3MN#EyOK zql(#yA}~Ibl*r(s1}Z^5mmn*-n93g?-ccM+^PN?6HH~h0hjy6@XY*^i<-V)+OZ;p7 z7j`p_sT55xnYsedNIIel^QIIg7i@`2Qi}x5$!tk29$2OQI zs^kQXAKE}5ZJu$)2@Dxn?}}O@f@6@^!%9Tj+o>=jd!^ZuvBE4jb4g}Z5WMBtcmy^~ zoFGVS5|0FA!(1Q%fL?Bj*L+9ZL{mjSO8lzqrQ0UCZ)X zPwk$1HNFgaK%NxGpuXz}#ywXvf2JQ?BQ5uOZM2up4S#ieaxS$!o9o6Z=czNQb} zwAh|xLZ>+WyN%o?^uCAQw&&4o?S$DJ`WP(Hr*grL*qNXlqU0osCQ(Up5F(^$Z5;n&oJIO4uF`k&QL*j{f zU=;#MZ5{@b%qMbjTB3dh-5#mqY>%{0jgS+WdHyG diff --git a/Pine/Assets/Square44x44Logo.scale-200.png b/Pine/Assets/Square44x44Logo.scale-200.png deleted file mode 100644 index f713bba67f551ef91020b75716a4dc8ebd744b1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 637 zcmeAS@N?(olHy`uVBq!ia0vp^5g^RL1|$oo8kjIJFu8cTIEGZ*dUI*J;2{SImxtDO zm%3!R$UazoY}x{$j0P5ABYXWr(l=jxJ6ps1W{tV=^>{Dl><3nv3A}sm=EZ)#l3`NR zpZda3^rNox*D1%NC98Z~L*6zipLw~Gxn&(Y-;KmJ+aR6eLabU-L#y8HW%7P-E_-VlLqIabbHPHKT*)fT@9iWJ7iWgOT9%0}Lrj>lztPxWq6sPw3pi z#-<=#$jjrP_DD*i!RLsn0mIA=>4~N)IMYWIf=j%-zuKCdMG%tHYot70D1| zvWa0wMhauW#S>1CnI_;>!1Q3zMA17@DOVq{MQ+{U7^a&yA+%dMCG;WNPV0i;w$tu; zX^b}UKziPM)(<;)ruW;-`)bBN+rQNM*Zs_>?n$|FVFo-e*PZb*@U7VAd+tHb4e?=Blc~}S6K)wL}r*Gf`BM#QB z+y>N$mCswb4d{^{S9v_!eQj4fTRMOwOCi?lSk9%<=vAz}jM-*PQtH@Odn1LZcd^j#o> hW$4xn+CT+ep9lJ{OAO?njobhL002ovPDHLkV1nYebbkN< diff --git a/Pine/Assets/StoreLogo.png b/Pine/Assets/StoreLogo.png deleted file mode 100644 index a4586f26bdf7841cad10f39cdffe2aca3af252c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 456 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2o;fF!p=8IEGZ*dUM0H=rDtTTVkd2 z(%lbKn@VS_lUaADVB&;Z6F#LM+mPsa?e>FnHo;HND^!P`-lX%BH~FOg%y&x+t*x!? zg$#_1A1kgsSvO(fw`bOmo;lrJX8byO1j^gf7qohR%mmt z@L)WX;>gqgK|tWJvQ5j;4;=gt4HXVKSMYRv5RhY5vS~TqfK_NAP*r{h!!g^BZ;w4r z7CGdsai)y;fJQc`7{Zc2b==h%o`Op$|bg6a&nL{*m7-=0>k4M4-PXlU;G-?%*(*g>iFt^ U$m#7DfHB12>FVdQ&MBb@0G`#n8vpc0sq%A~kJcD9FY~qQRMt?ZR3YyDZt}Od;|mgpc{2dv9AHF){kXU%k({ z=Y8JidEayHTkG@twPZ|U3_^%3ct-OgLSiFAqDN!|tbCX@c@?4P`2x*TMK!+Q4b?k0 ziW7!!KF6dPWcF<%I|iznM~`QJ_V7sHGV_D`dhgpA9Vd@&X}ErK+j~_rdv;Bp?OA@a zFXOk7eWOJe5NcK;6h$FaM&7JxNc#-@QTwzW6x#d_zmQNkz5) zPI;kh;3d;5UCJU+9a(cOxX(|edWoOiAEdGU#kPJ&xnc2||3vDbuhBCkj-pb0as$Zl z5;}4n=**n6(1g`JEtSy;SG6X;#-F~Oz3lESG2b5`j@wAwY4Yp<=4Xeb>iH=6aicF?DxD&q{`!&}ct zBI)aycwuobQAf&678Uf+Mmh-@9RUhyH~>?w0dixO0#jZjEc9R^=5NZw=|a(kcB?9^ zfnTiEFXp-q#B;Tn>(O%$A*ud^Rg&eVH6Y_5Y%!E39RR&s?XpG`gKwU!6FE1 z7X)DC7)*(5g}lh`4`{i~DZcWupZI`K)_4P)VE{@gc7@Xsd^86zl~_mOYH?I4!aGeX z^E(_=L6?PgveDQ+r%P@UISEXrkn`LHJZ##+!-anV>6h)IkKp;E@p8+3&(5%kS2)ld*J*rJccZM0iyaAx7+F~GW1UWFK&3X$PE1^}NH zgAG9ck5K!{07OwU@j@Do>TbH=CDEo#4m0cEyAuXy_<&jlzJVcKweSJ5 z&=q~iIn18$w8yb=rmEmHxVEUA^?RwnB?6Qlp1os8@*dWTGL2bhzZ!s*xqScR?EPL` zo(JwNdKUUYy7GtvZ3asXm)cgFvCx9EmAi;|w=a0iGiv%%VYKh`P0Wma4y`Xyx|T~( zAmfGbgbEEC7)j8b@WA@+5W3a61HJXC1dX@6_T|Czk0I0zBk%tnW~()VWITGI!`$c< gARL?UBrYYkwoDw4eo*CrzXGTrZ@;GF>596)00d&n@&Et; diff --git a/Pine/Client/PineClient.cs b/Pine/Client/PineClient.cs deleted file mode 100644 index c74e541..0000000 --- a/Pine/Client/PineClient.cs +++ /dev/null @@ -1,136 +0,0 @@ -using Pine.SocketMessages; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Net.Sockets; -using System.Text; -using System.Threading.Tasks; - -namespace Pine.Client -{ - public class PineClient - { - private readonly TcpClient tcpClient; - private NetworkStream stream; - - public PineClient() - { - tcpClient = new TcpClient(); - } - - ~PineClient() - { - tcpClient.Close(); - tcpClient.Dispose(); - } - - public async Task ConnectAsync(string host, int port) - { - try - { - await tcpClient.ConnectAsync(host, port); - - - if (tcpClient.Connected) - stream = tcpClient.GetStream(); - else - return false; - - await ValidateConnection(); - - if (!await CheckVersion()) - return false; - - OnConnected?.Invoke(this, tcpClient.Connected); - - return tcpClient.Connected; - - } - catch (SocketException e) - { - OnConnectionFailed?.Invoke(this, (Int32)e.SocketErrorCode); - - return false; - } - } - - public async Task ReceiveRawMessage(UInt64 size) - { - byte[] buffer = new byte[size]; - await stream.ReadAsync(buffer); - return buffer; - } - - public async Task SendRawMessage(byte[] buffer) - { - await tcpClient.Client.SendAsync(buffer, SocketFlags.None); - } - - public async Task ReceiveMessage() - { - Message message = new(); - - byte[] header = await ReceiveRawMessage(MessageHeader.Size); - MessageHeader messageHeader = new(header); - - if (messageHeader.Type == MessageType.Invalid) - return message; - - byte[] body = await ReceiveRawMessage(messageHeader.BodySize); - - if (messageHeader.Type == MessageType.Hello) - { - HelloMessage hello = new(); - - if (!hello.ParseBody(body)) - return message; - - message = hello; - } - else - return message; - - message.Header = messageHeader; - - return message; - } - - public async Task SendMessage(Message message) - { - await SendRawMessage(message.Serialize()); - } - - public async Task CheckVersion() - { - Message helloReceived = await ReceiveMessage(); - - if (helloReceived.Header.Type != MessageType.Hello) - return false; - - if (((HelloMessage)helloReceived).Version != HelloMessage.CURRENT_VERSION) - return false; - - HelloMessage hello = new() - { - Version = HelloMessage.CURRENT_VERSION - }; - - await SendRawMessage(hello.Serialize()); - - return true; - } - - public async Task ValidateConnection() - { - byte[] buffer = await ReceiveRawMessage(8); - - UInt64 key = BitConverter.ToUInt64(buffer) ^ 0xF007CAFEC0C0CA7E; - - await SendRawMessage(BitConverter.GetBytes(key)); - } - - public event EventHandler OnConnected; - public event EventHandler OnConnectionFailed; - } -} diff --git a/Pine/Package.appxmanifest b/Pine/Package.appxmanifest deleted file mode 100644 index 08438f6..0000000 --- a/Pine/Package.appxmanifest +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - Pine - Jacquwes - Assets\StoreLogo.png - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pine/Pine.csproj b/Pine/Pine.csproj deleted file mode 100644 index 652e832..0000000 --- a/Pine/Pine.csproj +++ /dev/null @@ -1,80 +0,0 @@ - - - WinExe - net6.0-windows10.0.19041.0 - 10.0.17763.0 - Pine - app.manifest - x86;x64;ARM64 - win10-x86;win10-x64;win10-arm64 - win10-$(Platform).pubxml - true - true - fr-FR - F62AF7D470EBFA86DAAB336F44CF926A87B4DEEB - Pine_TemporaryKey.pfx - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - MSBuild:Compile - - - - - - true - - diff --git a/Pine/Properties/launchSettings.json b/Pine/Properties/launchSettings.json deleted file mode 100644 index c26d684..0000000 --- a/Pine/Properties/launchSettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "profiles": { - "Pine (Package)": { - "commandName": "MsixPackage" - }, - "Pine (Unpackaged)": { - "commandName": "Project" - } - } -} \ No newline at end of file diff --git a/Pine/SocketMessages/HelloMessage.cs b/Pine/SocketMessages/HelloMessage.cs deleted file mode 100644 index cf0c90f..0000000 --- a/Pine/SocketMessages/HelloMessage.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace Pine.SocketMessages -{ - public class HelloMessage : Message - { - public static readonly UInt64 CURRENT_VERSION = 0x0; - - public HelloMessage() - { - Header.Type = MessageType.Hello; - Header.BodySize = Size; - } - - public override bool ParseBody(byte[] buffer) - { - if ((UInt64)buffer.LongLength != Size) - return false; - - Version = BitConverter.ToUInt64(buffer, 0); - return true; - } - - public override byte[] Serialize() - { - byte[] buffer = Array.Empty(); - buffer = buffer.Concat(Header.Serialize()).ToArray(); - - buffer = buffer.Concat(BitConverter.GetBytes(Version)).ToArray(); - - return buffer; - } - - public static readonly UInt64 Size = sizeof(UInt64); - public UInt64 Version { get; set; } = 0; - } -} diff --git a/Pine/SocketMessages/LoginMessage.cs b/Pine/SocketMessages/LoginMessage.cs deleted file mode 100644 index 3e75600..0000000 --- a/Pine/SocketMessages/LoginMessage.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace Pine.SocketMessages -{ - internal class LoginMessage : Message - { - public LoginMessage() - { - Header.Type = MessageType.Login; - Header.BodySize = Size; - } - - public override bool ParseBody(byte[] buffer) - { - if ((UInt64)buffer.LongLength != Size) - return false; - - Username = Encoding.ASCII.GetString(buffer); - return true; - } - - public override byte[] Serialize() - { - byte[] buffer = Array.Empty(); - buffer = buffer.Concat(Header.Serialize()).ToArray(); - - buffer = buffer.Concat(Encoding.ASCII.GetBytes(Username)).ToArray(); - Array.Resize(ref buffer, (int)MessageHeader.Size + (int)Size); - - return buffer; - } - - public bool SetUsername(string username) - { - if (username.Length > UsernameMaxLength) - return false; - Username = username; - return true; - } - - public static readonly int UsernameMaxLength = 0x20; - - public string Username { get; private set; } = new('\0', UsernameMaxLength); - public static readonly UInt64 Size = (UInt64)UsernameMaxLength; - } -} diff --git a/Pine/SocketMessages/Message.cs b/Pine/SocketMessages/Message.cs deleted file mode 100644 index e99bef2..0000000 --- a/Pine/SocketMessages/Message.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Pine.SocketMessages -{ - public enum MessageType : byte - { - Invalid, - Hello, - Login - } - - public class MessageHeader - { - public MessageHeader() - { - Type = MessageType.Invalid; - } - - public MessageHeader(byte[] buffer) - { - Parse(buffer); - } - - public void Parse(byte[] buffer) - { - BodySize = BitConverter.ToUInt64(buffer, 1); - - byte messageType = buffer[0]; - switch (messageType) - { - case (byte)MessageType.Hello: - Type = MessageType.Hello; - if (BodySize != HelloMessage.Size) - Type = MessageType.Invalid; - break; - case (byte)MessageType.Login: - Type = MessageType.Login; - break; - default: - Type = MessageType.Invalid; - break; - } - } - - public byte[] Serialize() - { - byte[] buffer = new byte[9]; - buffer[0] = (byte)Type; - Array.Copy(BitConverter.GetBytes(BodySize), 0, buffer, 1, 8); - return buffer; - } - - public MessageType Type { get; set; } - public UInt64 BodySize { get; set; } - - public static readonly UInt64 Size = sizeof(MessageType) + sizeof(UInt64); - } - - public class Message - { - public MessageHeader Header { get; set; } - - public Message() - { - Header = new(); - } - - public virtual bool ParseBody(byte[] buffer) - { - return false; - } - - public virtual byte[] Serialize() - { - return new byte[9]; - } - } -} diff --git a/Pine/Views/Identify/IdentifyControl.xaml b/Pine/Views/Identify/IdentifyControl.xaml deleted file mode 100644 index 2db5c03..0000000 --- a/Pine/Views/Identify/IdentifyControl.xaml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - -