From 4d2464c1f5813e2e9014444f3282bbe15888fcff Mon Sep 17 00:00:00 2001 From: jmsgrogan Date: Fri, 27 Jan 2023 17:04:39 +0000 Subject: [PATCH] Add win socket client and server. --- src/base/core/CMakeLists.txt | 5 + src/base/core/ThreadCollection.cpp | 76 ++++++ src/base/core/ThreadCollection.h | 31 +++ src/base/core/http/HttpParser.cpp | 43 +++ src/base/core/http/HttpParser.h | 9 + src/base/core/http/HttpPreamble.h | 10 + src/base/core/http/HttpRequest.cpp | 81 +++--- src/base/core/http/HttpRequest.h | 18 +- src/base/core/http/HttpResponse.cpp | 33 +++ src/base/core/http/HttpResponse.h | 14 + src/base/network/CMakeLists.txt | 36 ++- src/base/network/NetworkManager.cpp | 61 +---- src/base/network/NetworkManager.h | 16 +- src/base/network/client/HttpClient.cpp | 48 ++++ src/base/network/client/HttpClient.h | 24 ++ .../network/client/PlatformSocketClient.h | 30 +++ .../UnixSocketClient.cpp} | 0 .../UnixSocketClient.h} | 0 .../network/client/win32/WinsockClient.cpp | 28 ++ src/base/network/client/win32/WinsockClient.h | 19 ++ src/base/network/server/HttpServer.cpp | 87 +++++++ src/base/network/server/HttpServer.h | 32 +++ .../network/server/PlatformSocketServer.h | 37 +++ src/base/network/server/UnixSocketServer.cpp | 0 src/base/network/server/UnixSocketServer.h | 0 src/base/network/server/WinsockServer.cpp | 77 ++++++ src/base/network/server/WinsockServer.h | 31 +++ src/base/network/sockets/BerkeleySocket.cpp | 0 src/base/network/sockets/BerkeleySocket.h | 0 src/base/network/sockets/IPlatformSocket.h | 0 src/base/network/sockets/Socket.cpp | 45 +--- src/base/network/sockets/Socket.h | 50 +++- src/base/network/sockets/SocketInterface.h | 7 +- src/base/network/sockets/WinsockInterface.cpp | 29 +++ src/base/network/sockets/WinsockInterface.h | 13 + src/base/network/sockets/WinsockSocket.cpp | 245 ++++++++++++++++++ src/base/network/sockets/WinsockSocket.h | 40 +++ src/base/network/web/HttpMessageHandler.cpp | 17 -- src/base/network/web/HttpMessageHandler.h | 9 - src/console/MainApplication.cpp | 5 - test/CMakeLists.txt | 4 +- test/network/CMakeLists.txt | 45 ++-- test/network/TestNetworkManagerClient.cpp | 12 - test/network/TestWinsockClient.cpp | 25 ++ test/network/TestWinsockServer.cpp | 21 ++ 45 files changed, 1167 insertions(+), 246 deletions(-) create mode 100644 src/base/core/ThreadCollection.cpp create mode 100644 src/base/core/ThreadCollection.h create mode 100644 src/base/core/http/HttpParser.cpp create mode 100644 src/base/core/http/HttpParser.h create mode 100644 src/base/core/http/HttpPreamble.h create mode 100644 src/base/network/client/HttpClient.cpp create mode 100644 src/base/network/client/HttpClient.h create mode 100644 src/base/network/client/PlatformSocketClient.h rename src/base/network/client/{win32/WinInetClient.cpp => unix/UnixSocketClient.cpp} (100%) rename src/base/network/client/{win32/WinInetClient.h => unix/UnixSocketClient.h} (100%) create mode 100644 src/base/network/client/win32/WinsockClient.cpp create mode 100644 src/base/network/client/win32/WinsockClient.h create mode 100644 src/base/network/server/HttpServer.cpp create mode 100644 src/base/network/server/HttpServer.h create mode 100644 src/base/network/server/PlatformSocketServer.h create mode 100644 src/base/network/server/UnixSocketServer.cpp create mode 100644 src/base/network/server/UnixSocketServer.h create mode 100644 src/base/network/server/WinsockServer.cpp create mode 100644 src/base/network/server/WinsockServer.h create mode 100644 src/base/network/sockets/BerkeleySocket.cpp create mode 100644 src/base/network/sockets/BerkeleySocket.h create mode 100644 src/base/network/sockets/IPlatformSocket.h create mode 100644 src/base/network/sockets/WinsockInterface.cpp create mode 100644 src/base/network/sockets/WinsockInterface.h create mode 100644 src/base/network/sockets/WinsockSocket.cpp create mode 100644 src/base/network/sockets/WinsockSocket.h delete mode 100644 src/base/network/web/HttpMessageHandler.cpp delete mode 100644 src/base/network/web/HttpMessageHandler.h delete mode 100644 test/network/TestNetworkManagerClient.cpp create mode 100644 test/network/TestWinsockClient.cpp create mode 100644 test/network/TestWinsockServer.cpp diff --git a/src/base/core/CMakeLists.txt b/src/base/core/CMakeLists.txt index 95aac1b..d3c607d 100644 --- a/src/base/core/CMakeLists.txt +++ b/src/base/core/CMakeLists.txt @@ -22,8 +22,11 @@ list(APPEND HEADERS http/HttpResponse.h http/HttpHeader.h http/HttpRequest.h + http/HttpParser.h + http/HttpPreamble.h serializers/TomlReader.h Win32BaseIncludes.h + ThreadCollection.h xml/XmlParser.h xml/XmlDocument.h xml/XmlWriter.h @@ -57,7 +60,9 @@ list(APPEND SOURCES http/HttpResponse.cpp http/HttpHeader.cpp http/HttpRequest.cpp + http/HttpParser.cpp serializers/TomlReader.cpp + ThreadCollection.cpp xml/XmlParser.cpp xml/XmlDocument.cpp xml/XmlWriter.cpp diff --git a/src/base/core/ThreadCollection.cpp b/src/base/core/ThreadCollection.cpp new file mode 100644 index 0000000..6394204 --- /dev/null +++ b/src/base/core/ThreadCollection.cpp @@ -0,0 +1,76 @@ +#include "ThreadCollection.h" + +#include + +bool ThreadCollection::add(std::unique_ptr thread) +{ + if (!mAccepting) + { + return false; + } + + std::scoped_lock guard(mMutex); + mThreads[thread->get_id()] = std::move(thread); + + return true; +} + +void ThreadCollection::joinAndClearAll() +{ + mAccepting = false; + std::vector threads; + { + std::scoped_lock guard(mMutex); + for (const auto& item : mThreads) + { + threads.push_back(item.second.get()); + } + } + + for (auto thread : threads) + { + if (thread->joinable()) + { + thread->join(); + } + } + + std::scoped_lock guard(mMutex); + mThreads.clear(); + mAccepting = true; +} + +std::size_t ThreadCollection::size() const +{ + std::size_t size{ 0 }; + { + std::scoped_lock guard(mMutex); + size = mThreads.size(); + } + return size; +} + +void ThreadCollection::markForRemoval(std::thread::thread::id inputId) +{ + std::scoped_lock guard(mMutex); + mMarkedForRemoval.push_back(inputId); +} + +void ThreadCollection::removeMarked() +{ + std::scoped_lock guard(mMutex); + for (const auto& id : mMarkedForRemoval) + { + _remove(id); + } + mMarkedForRemoval.clear(); +} + +void ThreadCollection::_remove(std::thread::thread::id inputId) +{ + if (auto const& it = mThreads.find(inputId); it != mThreads.end()) + { + it->second->detach(); + mThreads.erase(it); + } +} \ No newline at end of file diff --git a/src/base/core/ThreadCollection.h b/src/base/core/ThreadCollection.h new file mode 100644 index 0000000..d8412d9 --- /dev/null +++ b/src/base/core/ThreadCollection.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class ThreadCollection +{ +public: + bool add(std::unique_ptr thread); + + void joinAndClearAll(); + + void markForRemoval(std::thread::thread::id inputId); + + void removeMarked(); + + std::size_t size() const; + +private: + void _remove(std::thread::thread::id inputId); + + mutable std::mutex mMutex; + std::atomic mAccepting{ true }; + + std::vector mMarkedForRemoval; + std::unordered_map > mThreads; +}; \ No newline at end of file diff --git a/src/base/core/http/HttpParser.cpp b/src/base/core/http/HttpParser.cpp new file mode 100644 index 0000000..6b66639 --- /dev/null +++ b/src/base/core/http/HttpParser.cpp @@ -0,0 +1,43 @@ +#include "HttpParser.h" + +#include "StringUtils.h" + +bool HttpParser::parsePreamble(const std::string& line, HttpPreamble& preamble) +{ + bool inPath{ false }; + bool inMethod{ true }; + bool inProtocol{ false }; + + for (const auto c : line) + { + if (inPath) + { + if (StringUtils::isSpace(c)) + { + inPath = false; + inMethod = true; + } + else + { + preamble.mPath.push_back(c); + } + } + else if (inMethod) + { + if (StringUtils::isSpace(c)) + { + inMethod = false; + inProtocol = true; + } + else + { + preamble.mMethod.push_back(c); + } + } + else if (inProtocol) + { + preamble.mVersion.push_back(c); + } + } + return true; +} \ No newline at end of file diff --git a/src/base/core/http/HttpParser.h b/src/base/core/http/HttpParser.h new file mode 100644 index 0000000..78ec0d1 --- /dev/null +++ b/src/base/core/http/HttpParser.h @@ -0,0 +1,9 @@ +#pragma once + +#include "HttpPreamble.h" + +class HttpParser +{ +public: + static bool parsePreamble(const std::string& line, HttpPreamble& preamble); +}; \ No newline at end of file diff --git a/src/base/core/http/HttpPreamble.h b/src/base/core/http/HttpPreamble.h new file mode 100644 index 0000000..c92d9c7 --- /dev/null +++ b/src/base/core/http/HttpPreamble.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +struct HttpPreamble +{ + std::string mMethod; + std::string mPath; + std::string mVersion; +}; \ No newline at end of file diff --git a/src/base/core/http/HttpRequest.cpp b/src/base/core/http/HttpRequest.cpp index c16d93d..ce813a1 100644 --- a/src/base/core/http/HttpRequest.cpp +++ b/src/base/core/http/HttpRequest.cpp @@ -1,14 +1,14 @@ #include "HttpRequest.h" #include "StringUtils.h" +#include "HttpParser.h" #include HttpRequest::HttpRequest(Verb verb, const std::string& path) - : mVerb(verb), - mPath(path) + : mVerb(verb) { - + mPreamble.mPath = path; } HttpRequest::Verb HttpRequest::getVerb() const @@ -18,22 +18,38 @@ HttpRequest::Verb HttpRequest::getVerb() const std::string HttpRequest::getPath() const { - return mPath; + return mPreamble.mPath; } -void HttpRequest::parseMessage(const std::string& message) +std::string HttpRequest::toString(const std::string& host) const +{ + std::string out; + + if (mVerb == Verb::GET) + { + out += "GET"; + } + + auto path = mPreamble.mPath; + out += " /" + path + " HTTP/" + mHeader.getHttpVersion() + "\n"; + out += "Host: " + host + "\n"; + out += "Accept - Encoding: \n"; + return out; +} + +void HttpRequest::fromString(const std::string& message) { std::stringstream ss(message); std::string buffer; - bool firstLine {true}; + bool firstLine{ true }; std::vector headers; - while(std::getline(ss, buffer, '\n')) + while (std::getline(ss, buffer, '\n')) { if (firstLine) { - parseFirstLine(buffer); + HttpParser::parsePreamble(buffer, mPreamble); firstLine = false; } else @@ -41,45 +57,16 @@ void HttpRequest::parseMessage(const std::string& message) headers.push_back(buffer); } } - mHeader.parse(headers); -} - -void HttpRequest::parseFirstLine(const std::string& line) -{ - bool inPath{false}; - bool inMethod{true}; - bool inProtocol{false}; - - for (std::size_t idx=0; idx @@ -20,20 +21,23 @@ public: HttpRequest() = default; - HttpRequest(Verb verb, const std::string& path); + HttpRequest(Verb verb, const std::string& path = {}); Verb getVerb() const; std::string getPath() const; - void parseMessage(const std::string& message); + void fromString(const std::string& string); + + std::string toString(const std::string& host) const; + + std::size_t requiredBytes() const; private: - void parseFirstLine(const std::string& line); - Verb mVerb = Verb::UNKNOWN; + HttpHeader mHeader; - std::string mMethod; - std::string mPath; - std::string mProtocolVersion; + HttpPreamble mPreamble; + + unsigned mRequiredBytes{ 0 }; }; diff --git a/src/base/core/http/HttpResponse.cpp b/src/base/core/http/HttpResponse.cpp index da48a00..6f3a0d5 100644 --- a/src/base/core/http/HttpResponse.cpp +++ b/src/base/core/http/HttpResponse.cpp @@ -1,5 +1,10 @@ #include "HttpResponse.h" +#include "StringUtils.h" +#include "HttpParser.h" + +#include + HttpResponse::HttpResponse() : mStatusCode(200), mResponseReason("OK"), @@ -33,11 +38,39 @@ void HttpResponse::setBody(const std::string& body) mBody = body; } +void HttpResponse::fromMessage(const std::string& message) +{ + std::stringstream ss(message); + + std::string buffer; + bool firstLine{ true }; + + std::vector headers; + while (std::getline(ss, buffer, '\n')) + { + if (firstLine) + { + HttpParser::parsePreamble(buffer, mPreamble); + firstLine = false; + } + else + { + headers.push_back(buffer); + } + } + mHeader.parse(headers); +} + unsigned HttpResponse::getBodyLength() const { return unsigned(mBody.length()); } +void HttpResponse::setClientError(const ClientError& error) +{ + mClientError = error; +} + std::string HttpResponse::getHeaderString() const { std::string header = "HTTP/" + mHeader.getHttpVersion() + " " + std::to_string(mStatusCode) + " " + mResponseReason + "\n"; diff --git a/src/base/core/http/HttpResponse.h b/src/base/core/http/HttpResponse.h index 667df70..80dd903 100644 --- a/src/base/core/http/HttpResponse.h +++ b/src/base/core/http/HttpResponse.h @@ -1,16 +1,25 @@ #pragma once #include "HttpHeader.h" +#include "HttpPreamble.h" #include class HttpResponse { public: + struct ClientError + { + std::string mMessage; + int mCode{ -1 }; + }; + HttpResponse(); ~HttpResponse(); + void fromMessage(const std::string& message); + unsigned getBodyLength() const; const std::string& getBody() const; @@ -31,8 +40,13 @@ public: void setBody(const std::string& body); + void setClientError(const ClientError& error); + private: + HttpPreamble mPreamble; HttpHeader mHeader; + + ClientError mClientError; unsigned short mStatusCode{ 200 }; std::string mResponseReason{ }; std::string mBody; diff --git a/src/base/network/CMakeLists.txt b/src/base/network/CMakeLists.txt index c29b173..fcf5ad6 100644 --- a/src/base/network/CMakeLists.txt +++ b/src/base/network/CMakeLists.txt @@ -5,7 +5,15 @@ set(platform_LIBS) if(UNIX) list(APPEND platform_INCLUDES - sockets/UnixSocketInterface.cpp) + sockets/BerkeleySocket.h + sockets/BerkeleySocket.cpp + sockets/UnixSocketInterface.h + sockets/UnixSocketInterface.cpp + server/UnixSockerServer.h + server/UnixSockerServer.cpp + client/unix/UnixSocketClient.h + client/unix/UnixSocketClient.cpp + ) else() list(APPEND platform_INCLUDES server/win32/Win32WebServer.h @@ -18,24 +26,34 @@ list(APPEND platform_INCLUDES server/win32/Win32WebResponse.cpp server/win32/Win32Buffer.h server/win32/Win32Buffer.cpp - client/win32/WinInetClient.h - client/win32/WinInetClient.cpp + server/WinsockServer.h + server/WinsockServer.cpp + client/win32/WinsockClient.h + client/win32/WinsockClient.cpp + sockets/WinsockInterface.h + sockets/WinsockInterface.cpp + sockets/WinsockSocket.h + sockets/WinsockSocket.cpp ) - list(APPEND platform_LIBS Httpapi.lib) + list(APPEND platform_LIBS Httpapi.lib Ws2_32.lib) endif() list(APPEND HEADERS NetworkManager.h + client/HttpClient.h + client/PlatformSocketClient.h + server/HttpServer.h + server/PlatformSocketServer.h sockets/Socket.h sockets/SocketInterface.h - sockets/ISocketMessageHandler.h - web/HttpMessageHandler.h + sockets/IPlatformSocket.h ) list(APPEND SOURCES + client/HttpClient.cpp + server/HttpServer.cpp NetworkManager.cpp sockets/Socket.cpp - web/HttpMessageHandler.cpp ) add_library(${MODULE_NAME} SHARED ${SOURCES} ${platform_INCLUDES} ${HEADERS}) @@ -43,9 +61,11 @@ add_library(${MODULE_NAME} SHARED ${SOURCES} ${platform_INCLUDES} ${HEADERS}) target_include_directories(${MODULE_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/sockets - ${CMAKE_CURRENT_SOURCE_DIR}/web + ${CMAKE_CURRENT_SOURCE_DIR}/server ${CMAKE_CURRENT_SOURCE_DIR}/server/win32 + ${CMAKE_CURRENT_SOURCE_DIR}/client ${CMAKE_CURRENT_SOURCE_DIR}/client/win32 + ${CMAKE_CURRENT_SOURCE_DIR}/client/unix ) set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) target_link_libraries( ${MODULE_NAME} PUBLIC core ${platform_LIBS}) diff --git a/src/base/network/NetworkManager.cpp b/src/base/network/NetworkManager.cpp index 223fdc5..2568bb0 100644 --- a/src/base/network/NetworkManager.cpp +++ b/src/base/network/NetworkManager.cpp @@ -1,16 +1,8 @@ #include "NetworkManager.h" -#ifdef __linux__ -#include "UnixSocketInterface.h" -#else -#include "Win32WebServer.h" -#endif - NetworkManager::NetworkManager() - : mActiveSockets(), - mSocketInterface() { - + mHttpClient = std::make_unique(); } NetworkManager::~NetworkManager() @@ -23,52 +15,21 @@ std::unique_ptr NetworkManager::Create() return std::make_unique(); } -void NetworkManager::initialize() -{ -#ifdef __linux__ - mSocketInterface = UnixSocketInterface::Create(); -#endif -} - void NetworkManager::runHttpServer(AbstractWebApp* webApp) { -#ifdef _WIN32 - Win32WebServer server(webApp); - server.initialize(); - server.run(); -#else - (void)webApp; - - if (!mSocketInterface) + if (!mHttpServer) { - initialize(); + mHttpServer = std::make_unique(); } - auto socket = Socket::Create(); - mSocketInterface->initializeSocket(socket); - mSocketInterface->socketListen(socket); - mSocketInterface->run(socket); -#endif + HttpServer::Address address; + address.mHost = "127.0.0.1"; + address.mPort = 8000; + + mHttpServer->run(webApp, address); } -void NetworkManager::runHttpClient() +HttpClient* NetworkManager::getHttpClient() const { - if (!mSocketInterface) - { - initialize(); - } - - if (!mSocketInterface) - { - return; - } - - auto socket = Socket::Create(); - mSocketInterface->initializeSocket(socket, "127.0.0.1"); - mSocketInterface->socketWrite(socket, "Hello Friend"); -} - -void NetworkManager::shutDown() -{ - -} + return mHttpClient.get(); +} \ No newline at end of file diff --git a/src/base/network/NetworkManager.h b/src/base/network/NetworkManager.h index c643e8b..2535766 100644 --- a/src/base/network/NetworkManager.h +++ b/src/base/network/NetworkManager.h @@ -1,11 +1,11 @@ #pragma once -#include "Socket.h" -#include "SocketInterface.h" - #include #include +#include "HttpClient.h" +#include "HttpServer.h" + class AbstractWebApp; class NetworkManager @@ -17,17 +17,13 @@ public: static std::unique_ptr Create(); - void initialize(); + HttpClient* getHttpClient() const; void runHttpServer(AbstractWebApp* webApp); - void runHttpClient(); - - void shutDown(); - private: - std::vector mActiveSockets; - ISocketInterfaceUPtr mSocketInterface; + std::unique_ptr mHttpServer; + std::unique_ptr mHttpClient; }; using NetworkManagerUPtr = std::unique_ptr; diff --git a/src/base/network/client/HttpClient.cpp b/src/base/network/client/HttpClient.cpp new file mode 100644 index 0000000..e37eb26 --- /dev/null +++ b/src/base/network/client/HttpClient.cpp @@ -0,0 +1,48 @@ +#include "HttpClient.h" + +#include "FileLogger.h" + +#ifdef _WIN32 +#include "WinsockClient.h" +#else +#include "UnixSocketClient.h" +#endif + +HttpClient::HttpClient() +{ +#ifdef _WIN32 + mSocketClient = std::make_unique(); +#else + mSocketClient = std::make_unique(); +#endif +} + +HttpResponse HttpClient::makeRequest(const HttpRequest& request, const Address& address) +{ + PlatformSocketClient::Address socket_address; + socket_address.mHost = address.mHost; + socket_address.mPort = address.mPort; + socket_address.mPrefix = address.mPrefix; + + const auto message = request.toString(address.mHost); + + MLOG_INFO("Output http request: " << message); + + auto socket_response = mSocketClient->request(socket_address, message); + + HttpResponse response; + if (socket_response.mStatus == PlatformSocketClient::Result::Status::OK) + { + response.fromMessage(socket_response.mBody); + } + else + { + MLOG_ERROR("Http request client error: " << socket_response.mErrorMessage << " | with code: " << socket_response.mErrorCode); + + HttpResponse::ClientError error; + error.mMessage = socket_response.mErrorMessage; + error.mCode = socket_response.mErrorCode; + response.setClientError(error); + } + return response; +} \ No newline at end of file diff --git a/src/base/network/client/HttpClient.h b/src/base/network/client/HttpClient.h new file mode 100644 index 0000000..eadfd6c --- /dev/null +++ b/src/base/network/client/HttpClient.h @@ -0,0 +1,24 @@ +#pragma once + +#include "PlatformSocketClient.h" +#include "HttpRequest.h" +#include "HttpResponse.h" + +#include + +class HttpClient +{ +public: + struct Address + { + std::string mPrefix; + std::string mHost; + unsigned int mPort{ 8000 }; + }; + + HttpClient(); + HttpResponse makeRequest(const HttpRequest& request, const Address& address); + +private: + std::unique_ptr mSocketClient; +}; \ No newline at end of file diff --git a/src/base/network/client/PlatformSocketClient.h b/src/base/network/client/PlatformSocketClient.h new file mode 100644 index 0000000..3e2b843 --- /dev/null +++ b/src/base/network/client/PlatformSocketClient.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +class PlatformSocketClient +{ +public: + struct Address + { + std::string mPrefix; + std::string mHost; + unsigned int mPort{8000}; + }; + + struct Result + { + enum class Status + { + OK, + FAILED + }; + + Status mStatus{ Status::FAILED }; + std::string mErrorMessage; + int mErrorCode{ -1 }; + std::string mBody; + }; + + virtual Result request(const Address& address, const std::string& message) = 0; +}; \ No newline at end of file diff --git a/src/base/network/client/win32/WinInetClient.cpp b/src/base/network/client/unix/UnixSocketClient.cpp similarity index 100% rename from src/base/network/client/win32/WinInetClient.cpp rename to src/base/network/client/unix/UnixSocketClient.cpp diff --git a/src/base/network/client/win32/WinInetClient.h b/src/base/network/client/unix/UnixSocketClient.h similarity index 100% rename from src/base/network/client/win32/WinInetClient.h rename to src/base/network/client/unix/UnixSocketClient.h diff --git a/src/base/network/client/win32/WinsockClient.cpp b/src/base/network/client/win32/WinsockClient.cpp new file mode 100644 index 0000000..29cac5f --- /dev/null +++ b/src/base/network/client/win32/WinsockClient.cpp @@ -0,0 +1,28 @@ +#include "WinsockClient.h" + +WinsockClient::WinsockClient() + : mSocketInterface(std::make_unique()) +{ + mSocketInterface->initializeWinsock(); +} + +WinsockClient::Result WinsockClient::request(const Address& address, const std::string& message) +{ + WinsockClient::Result result; + + auto socket = std::make_unique(address.mHost, address.mPort); + auto response = socket->send(message); + + if (socket->getState().mConnectStatus != Socket::State::ConnectStatus::FAILED) + { + result.mStatus = WinsockClient::Result::Status::OK; + result.mBody = response; + } + else + { + result.mStatus = WinsockClient::Result::Status::FAILED; + result.mErrorCode = socket->getState().mErrorCode; + result.mErrorMessage = socket->getState().mErrorMessage; + } + return result; +} \ No newline at end of file diff --git a/src/base/network/client/win32/WinsockClient.h b/src/base/network/client/win32/WinsockClient.h new file mode 100644 index 0000000..a15601d --- /dev/null +++ b/src/base/network/client/win32/WinsockClient.h @@ -0,0 +1,19 @@ +#pragma once + +#include "WinsockSocket.h" +#include "WinsockInterface.h" + +#include "PlatformSocketClient.h" + +#include + +class WinsockClient : public PlatformSocketClient +{ +public: + WinsockClient(); + + Result request(const Address& address, const std::string& message); + +private: + std::unique_ptr mSocketInterface; +}; \ No newline at end of file diff --git a/src/base/network/server/HttpServer.cpp b/src/base/network/server/HttpServer.cpp new file mode 100644 index 0000000..795e0ab --- /dev/null +++ b/src/base/network/server/HttpServer.cpp @@ -0,0 +1,87 @@ +#include "HttpServer.h" + +#ifdef _WIN32 +#include "WinsockServer.h" +#else +#include "UnixSocketServer.h" +#endif + +#include "Socket.h" +#include "AbstractWebApp.h" + +#include "HttpRequest.h" +#include "HttpResponse.h" +#include "FileLogger.h" + +HttpServer::HttpServer() +{ +#ifdef _WIN32 + mSocketServer = std::make_unique(); +#else + mSocketServer = std::make_unique(); +#endif +} + +HttpServer::~HttpServer() +{ + +} + +void HttpServer::onConnection(Socket* socket) +{ + auto message = socket->recieve(); + + if (socket->getState().mConnectStatus == Socket::State::ConnectStatus::OK) + { + MLOG_INFO("Got client content: " << message); + + HttpRequest request; + request.fromString(message); + + std::string extra_bytes; + if (request.requiredBytes() > 0) + { + extra_bytes += socket->recieve(); + } + + auto response = mWebApp->onHttpRequest(request); + socket->respond(response.toString()); + } + else if (socket->getState().mConnectStatus == Socket::State::ConnectStatus::UNSET) + { + MLOG_INFO("Client closed connection"); + } + else + { + MLOG_INFO("Connection error"); + } +} + +void HttpServer::onFailure(const std::string& reason) +{ + MLOG_ERROR("Connection failed: " << reason); +} + +void HttpServer::run(AbstractWebApp* webApp, Address mListenAddress) +{ + mWebApp = webApp; + + PlatformSocketServer::Address socket_address; + socket_address.mHost = mListenAddress.mHost; + socket_address.mPort = mListenAddress.mPort; + socket_address.mPrefix = mListenAddress.mPrefix; + + auto on_connection = [this](Socket* socket) + { + this->onConnection(socket); + }; + + auto on_failure = [this](const PlatformSocketServer::Result& result) + { + this->onFailure(result.mErrorMessage); + }; + + mSocketServer->listen(socket_address, on_connection, on_failure); + + mSocketServer->shutDown(); +} \ No newline at end of file diff --git a/src/base/network/server/HttpServer.h b/src/base/network/server/HttpServer.h new file mode 100644 index 0000000..a9af188 --- /dev/null +++ b/src/base/network/server/HttpServer.h @@ -0,0 +1,32 @@ +#pragma once + +#include "PlatformSocketServer.h" + +#include + +class AbstractWebApp; +class Socket; + +class HttpServer +{ +public: + struct Address + { + std::string mPrefix; + std::string mHost; + unsigned int mPort{ 8000 }; + }; + + HttpServer(); + ~HttpServer(); + + void run(AbstractWebApp* webApp, Address mListenAddress); + +private: + void onConnection(Socket* socket); + + void onFailure(const std::string& reason); + + AbstractWebApp* mWebApp{ nullptr }; + std::unique_ptr mSocketServer; +}; \ No newline at end of file diff --git a/src/base/network/server/PlatformSocketServer.h b/src/base/network/server/PlatformSocketServer.h new file mode 100644 index 0000000..d0f7f41 --- /dev/null +++ b/src/base/network/server/PlatformSocketServer.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +class Socket; + +class PlatformSocketServer +{ +public: + struct Address + { + std::string mPrefix; + std::string mHost; + unsigned int mPort{8000}; + }; + + struct Result + { + enum class Status + { + OK, + FAILED + }; + + Status mStatus{ Status::FAILED }; + std::string mErrorMessage; + int mErrorCode{ -1 }; + std::string mBody; + }; + + using onConnectionSuccessFunc = std::function; + using onConnectionFailedFunc = std::function; + virtual void listen(const Address& address, onConnectionSuccessFunc connectionSuccessFunc, onConnectionFailedFunc connectionFailedFunc) = 0; + + virtual void shutDown() {}; +}; \ No newline at end of file diff --git a/src/base/network/server/UnixSocketServer.cpp b/src/base/network/server/UnixSocketServer.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/base/network/server/UnixSocketServer.h b/src/base/network/server/UnixSocketServer.h new file mode 100644 index 0000000..e69de29 diff --git a/src/base/network/server/WinsockServer.cpp b/src/base/network/server/WinsockServer.cpp new file mode 100644 index 0000000..67f30b1 --- /dev/null +++ b/src/base/network/server/WinsockServer.cpp @@ -0,0 +1,77 @@ +#include "WinsockServer.h" + +#include "WinsockSocket.h" +#include "WinsockInterface.h" + +#include "FileLogger.h" + +#include + +WinsockServer::~WinsockServer() +{ + +} + +void WinsockServer::listen(const Address& address, onConnectionSuccessFunc connectionSuccessFunc, onConnectionFailedFunc connectionFailedFunc) +{ + if (!mWinsockInterface) + { + mWinsockInterface = std::make_unique(); + mWinsockInterface->initializeWinsock(); + } + + mConnectionCallback = connectionSuccessFunc; + mFailedCallback = connectionFailedFunc; + + auto server_socket = std::make_unique(address.mHost, address.mPort); + + auto on_connection = [this](SOCKET handle) + { + auto socket = std::make_unique(handle); + this->onConnection(std::move(socket)); + }; + server_socket->doListen(on_connection); + + if (server_socket->getState().mBindStatus == Socket::State::BindStatus::FAILED) + { + WinsockServer::Result result; + result.mStatus = WinsockServer::Result::Status::FAILED; + result.mErrorCode = server_socket->getState().mErrorCode; + result.mErrorMessage = server_socket->getState().mErrorMessage; + mFailedCallback(result); + } +} + +void WinsockServer::shutDown() +{ + mThreads.removeMarked(); + mThreads.joinAndClearAll(); +} + +void WinsockServer::onConnection(std::unique_ptr s) +{ + // House-keeping first - clean up any finished threads + MLOG_INFO("Before thread cleanup: " << mThreads.size()); + + mThreads.removeMarked(); + + MLOG_INFO("After thread cleanup: " << mThreads.size()); + + auto worker_func = [this](std::unique_ptr s) + { + MLOG_INFO("Spawned thread for new connection"); + + mConnectionCallback(s.get()); + + MLOG_INFO("Finished thread for new connection"); + this->onThreadComplete(std::this_thread::get_id()); + }; + + auto worker = std::make_unique(worker_func, std::move(s)); + mThreads.add(std::move(worker)); +}; + +void WinsockServer::onThreadComplete(std::thread::id id) +{ + mThreads.markForRemoval(id); +} \ No newline at end of file diff --git a/src/base/network/server/WinsockServer.h b/src/base/network/server/WinsockServer.h new file mode 100644 index 0000000..0fe7ced --- /dev/null +++ b/src/base/network/server/WinsockServer.h @@ -0,0 +1,31 @@ +#pragma once + +#include "PlatformSocketServer.h" +#include "ThreadCollection.h" +#include "WinsockInterface.h" + +#include + +class WinsockSocket; +class WinsockInterface; + +class WinsockServer : public PlatformSocketServer +{ +public: + virtual ~WinsockServer(); + + void listen(const Address& address, onConnectionSuccessFunc connectionSuccessFunc, onConnectionFailedFunc connectionFailedFunc) override; + + void shutDown() override; + +private: + void onConnection(std::unique_ptr clientHandle); + + void onThreadComplete(std::thread::id id); + + ThreadCollection mThreads; + onConnectionSuccessFunc mConnectionCallback; + onConnectionFailedFunc mFailedCallback; + + std::unique_ptr mWinsockInterface; +}; \ No newline at end of file diff --git a/src/base/network/sockets/BerkeleySocket.cpp b/src/base/network/sockets/BerkeleySocket.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/base/network/sockets/BerkeleySocket.h b/src/base/network/sockets/BerkeleySocket.h new file mode 100644 index 0000000..e69de29 diff --git a/src/base/network/sockets/IPlatformSocket.h b/src/base/network/sockets/IPlatformSocket.h new file mode 100644 index 0000000..e69de29 diff --git a/src/base/network/sockets/Socket.cpp b/src/base/network/sockets/Socket.cpp index 663a566..e81c58d 100644 --- a/src/base/network/sockets/Socket.cpp +++ b/src/base/network/sockets/Socket.cpp @@ -1,10 +1,8 @@ #include "Socket.h" -Socket::Socket() - : mHandle(-1), - mPort(8888), - mMessage() - +Socket::Socket(const std::string& address, unsigned port) + : mPort(port), + mAddress(address) { } @@ -14,34 +12,9 @@ Socket::~Socket() } -void Socket::setHandle(SocketHandle handle) +const Socket::State& Socket::getState() const { - mHandle = handle; -} - -Socket::SocketHandle Socket::getHandle() const -{ - return mHandle; -} - -std::unique_ptr Socket::Create() -{ - return std::make_unique(); -} - -std::string Socket::getMessage() const -{ - return mMessage; -} - -void Socket::setMessage(const std::string& message) -{ - mMessage = message; -} - -void Socket::setPort(unsigned port) -{ - mPort = port; + return mState; } unsigned Socket::getPort() const @@ -52,10 +25,4 @@ unsigned Socket::getPort() const std::string Socket::getAddress() const { return mAddress; -} - - -void Socket::setAddress(const std::string& address) -{ - mAddress = address; -} +} \ No newline at end of file diff --git a/src/base/network/sockets/Socket.h b/src/base/network/sockets/Socket.h index 51aeb04..bf83477 100644 --- a/src/base/network/sockets/Socket.h +++ b/src/base/network/sockets/Socket.h @@ -2,38 +2,60 @@ #include #include +#include class Socket { - using SocketHandle = int; public: + struct State + { + enum class ConnectStatus + { + UNSET, + OK, + FAILED + }; - Socket(); + enum class BindStatus + { + UNSET, + OK, + FAILED + }; - ~Socket(); + ConnectStatus mConnectStatus{ ConnectStatus::UNSET }; + BindStatus mBindStatus{ BindStatus::UNSET }; + std::string mErrorMessage; + int mErrorCode{ 0 }; + std::string mBody; + }; - static std::unique_ptr Create(); + Socket(const std::string& address, unsigned port); + + virtual ~Socket(); std::string getAddress() const; - SocketHandle getHandle() const; - unsigned getPort() const; - std::string getMessage() const; + const State& getState() const; - void setPort(unsigned port); + virtual void respond(const std::string& message) = 0; - void setHandle(SocketHandle handle); + virtual std::string recieve() = 0; - void setMessage(const std::string& message); + virtual std::string send(const std::string& message) = 0; +protected: + virtual void initialize() {}; - void setAddress(const std::string& address); + virtual void initializeForBind() {}; -private: - SocketHandle mHandle; + virtual void doBind() {}; + + virtual void doConnect() {}; + + State mState; unsigned mPort{0}; - std::string mMessage; std::string mAddress; }; diff --git a/src/base/network/sockets/SocketInterface.h b/src/base/network/sockets/SocketInterface.h index d945bc3..ccd31c3 100644 --- a/src/base/network/sockets/SocketInterface.h +++ b/src/base/network/sockets/SocketInterface.h @@ -1,6 +1,7 @@ #pragma once #include +#include class Socket; using SocketPtr = std::unique_ptr; @@ -11,12 +12,6 @@ public: ISocketInterface() = default; virtual ~ISocketInterface() = default; - - virtual void initializeSocket(const SocketPtr& socket, const std::string& address = {}) = 0; - virtual void socketListen(const SocketPtr& socket) = 0; - virtual void run(const SocketPtr& socket) = 0; - - virtual void socketWrite(const SocketPtr& socket, const std::string& message) = 0; }; using ISocketInterfaceUPtr = std::unique_ptr; diff --git a/src/base/network/sockets/WinsockInterface.cpp b/src/base/network/sockets/WinsockInterface.cpp new file mode 100644 index 0000000..42db306 --- /dev/null +++ b/src/base/network/sockets/WinsockInterface.cpp @@ -0,0 +1,29 @@ +#include "WinsockInterface.h" + +#include "FileLogger.h" + +#include +#include + +WinsockInterface::~WinsockInterface() +{ + closeWinsock(); +} + +bool WinsockInterface::initializeWinsock() +{ + WSADATA wsaData; + + auto iResult = ::WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) + { + MLOG_ERROR("WSAStartup failed: " << iResult); + return false; + } + return true; +} + +void WinsockInterface::closeWinsock() +{ + ::WSACleanup(); +} \ No newline at end of file diff --git a/src/base/network/sockets/WinsockInterface.h b/src/base/network/sockets/WinsockInterface.h new file mode 100644 index 0000000..ce64a00 --- /dev/null +++ b/src/base/network/sockets/WinsockInterface.h @@ -0,0 +1,13 @@ +#pragma once + +#include "SocketInterface.h" + +class WinsockInterface : public ISocketInterface +{ +public: + ~WinsockInterface(); + + bool initializeWinsock(); + + void closeWinsock(); +}; \ No newline at end of file diff --git a/src/base/network/sockets/WinsockSocket.cpp b/src/base/network/sockets/WinsockSocket.cpp new file mode 100644 index 0000000..7cb0a68 --- /dev/null +++ b/src/base/network/sockets/WinsockSocket.cpp @@ -0,0 +1,245 @@ +#include "WinsockSocket.h" + +#include +#include + +#include "FileLogger.h" + +WinsockSocket::WinsockSocket(const std::string& address, unsigned port) + : Socket(address, port) +{ + +} + +WinsockSocket::WinsockSocket(SOCKET handle) + : Socket("", 0), + mHandle(handle) +{ + mState.mConnectStatus = State::ConnectStatus::OK; +} + +WinsockSocket::~WinsockSocket() +{ + MLOG_INFO("Socket being destroyed"); +} + +void WinsockSocket::initialize() +{ + addrinfo hints; + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + auto result = ::getaddrinfo(mAddress.c_str(), std::to_string(mPort).c_str(), &hints, &mAddressInfo); + if (result != 0) + { + mState.mConnectStatus = Socket::State::ConnectStatus::FAILED; + mState.mErrorCode = result; + mState.mErrorMessage = "WinsockSocket: getaddrinfo failed for connect"; + return; + } + + mHandle = ::socket(mAddressInfo->ai_family, mAddressInfo->ai_socktype, mAddressInfo->ai_protocol); + if (mHandle == INVALID_SOCKET) + { + onSockerError("WinsockSocket: Error at socket()"); + ::freeaddrinfo(mAddressInfo); + return; + } +} + +void WinsockSocket::initializeForBind() +{ + addrinfo hints; + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + // https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 + auto result = ::getaddrinfo(nullptr, std::to_string(mPort).c_str(), &hints, &mAddressInfo); + if (result != 0) + { + mState.mBindStatus = Socket::State::BindStatus::FAILED; + mState.mErrorCode = result; + mState.mErrorMessage = "WinsockSocket: getaddrinfo failed for bind"; + return; + } + + mHandle = ::socket(mAddressInfo->ai_family, mAddressInfo->ai_socktype, mAddressInfo->ai_protocol); + if (mHandle == INVALID_SOCKET) + { + onSockerError("WinsockSocket: Error at socket()"); + ::freeaddrinfo(mAddressInfo); + return; + } +} + +void WinsockSocket::doConnect() +{ + auto result = ::connect(mHandle, mAddressInfo->ai_addr, (int)mAddressInfo->ai_addrlen); + if (result == SOCKET_ERROR) + { + ::closesocket(mHandle); + mHandle = INVALID_SOCKET; + } + + ::freeaddrinfo(mAddressInfo); + + if (mHandle == INVALID_SOCKET) + { + mState.mConnectStatus = Socket::State::ConnectStatus::FAILED; + mState.mErrorCode = SOCKET_ERROR; + mState.mErrorMessage = "WinsockSocket: Unable to connect to server."; + return; + } + mState.mConnectStatus = Socket::State::ConnectStatus::OK; +} + +void WinsockSocket::doBind() +{ + auto result = ::bind(mHandle, mAddressInfo->ai_addr, (int)mAddressInfo->ai_addrlen); + if (result == SOCKET_ERROR) + { + ::closesocket(mHandle); + mHandle = INVALID_SOCKET; + } + + ::freeaddrinfo(mAddressInfo); + + if (mHandle == INVALID_SOCKET) + { + mState.mBindStatus = Socket::State::BindStatus::FAILED; + mState.mErrorCode = ::WSAGetLastError(); + mState.mErrorMessage = "WinsockSocket: Unable to bind socket"; + return; + } + mState.mBindStatus = Socket::State::BindStatus::OK; +} + +void WinsockSocket::doListen(onIncomingConnectionFunc connectionFunc) +{ + initializeForBind(); + if (mState.mBindStatus == Socket::State::BindStatus::FAILED) + { + return; + } + + doBind(); + if (mState.mBindStatus == Socket::State::BindStatus::FAILED) + { + return; + } + + if (::listen(mHandle, SOMAXCONN) == SOCKET_ERROR) + { + mState.mBindStatus = Socket::State::BindStatus::FAILED; + mState.mErrorCode = ::WSAGetLastError(); + mState.mErrorMessage = "WinsockSocket: Listen failed"; + ::closesocket(mHandle); + return; + } + + while (true) + { + auto client_handle = ::accept(mHandle, NULL, NULL); + if (client_handle == INVALID_SOCKET) + { + mState.mBindStatus = Socket::State::BindStatus::FAILED; + mState.mErrorCode = ::WSAGetLastError(); + mState.mErrorMessage = "WinsockSocket: Accept failed"; + ::closesocket(mHandle); + break; + } + else + { + connectionFunc(client_handle); + } + } +} + +std::string WinsockSocket::send(const std::string& message) +{ + if (mState.mConnectStatus != Socket::State::ConnectStatus::OK) + { + initialize(); + if (mState.mConnectStatus == Socket::State::ConnectStatus::FAILED) + { + return {}; + } + + doConnect(); + if (mState.mConnectStatus == Socket::State::ConnectStatus::FAILED) + { + return {}; + } + } + + auto result = ::send(mHandle, message.c_str(), static_cast(message.size()), 0); + if (result == SOCKET_ERROR) + { + onSockerError("WinsockSocket: Send failed."); + return {}; + } + + result = ::shutdown(mHandle, SD_SEND); + if (result == SOCKET_ERROR) + { + onSockerError("WinsockSocket: Post send shutdown failed."); + return {}; + } + + std::string response; + while (mState.mConnectStatus == Socket::State::ConnectStatus::OK) + { + response += recieve(); + } + ::closesocket(mHandle); + + return response; +} + +void WinsockSocket::respond(const std::string& message) +{ + auto result = ::send(mHandle, message.c_str(), static_cast(message.size()), 0); + if (result == SOCKET_ERROR) + { + onSockerError("WinsockSocket: Respond failed."); + } +} + +void WinsockSocket::onSockerError(const std::string& message) +{ + mState.mErrorCode = ::WSAGetLastError(); + mState.mErrorMessage = message; + + if (mState.mConnectStatus == Socket::State::ConnectStatus::OK) + { + ::closesocket(mHandle); + } + mState.mConnectStatus = Socket::State::ConnectStatus::FAILED; +} + +std::string WinsockSocket::recieve() +{ + const int BUFFER_SIZE = 512; + char buffer[BUFFER_SIZE]; + auto result = ::recv(mHandle, buffer, BUFFER_SIZE, 0); + if (result > 0) + { + return std::string(buffer); + } + else if (result == 0) + { + mState.mConnectStatus = Socket::State::ConnectStatus::UNSET; + } + else + { + mState.mConnectStatus = Socket::State::ConnectStatus::FAILED; + mState.mErrorCode = ::WSAGetLastError(); + mState.mErrorMessage = "WinsockSocket: recv failed."; + } + return {}; +} diff --git a/src/base/network/sockets/WinsockSocket.h b/src/base/network/sockets/WinsockSocket.h new file mode 100644 index 0000000..d23fc3b --- /dev/null +++ b/src/base/network/sockets/WinsockSocket.h @@ -0,0 +1,40 @@ +#pragma once + +#include "Socket.h" + +#include + +#include + +class WinsockSocket : public Socket +{ +public: + WinsockSocket(const std::string& address, unsigned port); + + WinsockSocket(SOCKET handle); + + ~WinsockSocket(); + + std::string recieve() override; + + void respond(const std::string& message) override; + + std::string send(const std::string& message) override; + + using onIncomingConnectionFunc = std::function; + void doListen(onIncomingConnectionFunc connectionFunc); + +private: + void initialize() override; + void initializeForBind() override; + + void doConnect() override; + + void doBind() override; + + void onSockerError(const std::string& message); + + SOCKET mHandle{ INVALID_SOCKET }; + + addrinfo* mAddressInfo{ nullptr }; +}; \ No newline at end of file diff --git a/src/base/network/web/HttpMessageHandler.cpp b/src/base/network/web/HttpMessageHandler.cpp deleted file mode 100644 index e664423..0000000 --- a/src/base/network/web/HttpMessageHandler.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "HttpMessageHandler.h" - -#include "HttpRequest.h" -#include "HttpResponse.h" - -std::string HttpMessageHandler::onMessage(const std::string& message) -{ - HttpRequest request; - request.parseMessage(message); - - HttpResponse response; - response.setBody("Hello world!"); - - const auto response_message = response.toString(); - - return response_message; -} diff --git a/src/base/network/web/HttpMessageHandler.h b/src/base/network/web/HttpMessageHandler.h deleted file mode 100644 index 60d3e17..0000000 --- a/src/base/network/web/HttpMessageHandler.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "ISocketMessageHandler.h" - -class HttpMessageHandler : public ISocketMessageHandler -{ -public: - std::string onMessage(const std::string& message) override; -}; diff --git a/src/console/MainApplication.cpp b/src/console/MainApplication.cpp index 2813735..62a6c18 100644 --- a/src/console/MainApplication.cpp +++ b/src/console/MainApplication.cpp @@ -152,11 +152,6 @@ void MainApplication::shutDown() mDatabaseManager->onShutDown(); } - if (mNetworkManager) - { - mNetworkManager->shutDown(); - } - MLOG_INFO("Shut down"); FileLogger::GetInstance().Close(); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c8b9aa5..8c8bc9d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(test_utils) add_subdirectory(fonts) add_subdirectory(geometry) add_subdirectory(graphics) +add_subdirectory(network) add_subdirectory(publishing) add_subdirectory(ui_controls) @@ -15,8 +16,7 @@ set(TEST_MODULES core database image - ipc - network + ipc mesh video web diff --git a/test/network/CMakeLists.txt b/test/network/CMakeLists.txt index 7209c0c..6ce556e 100644 --- a/test/network/CMakeLists.txt +++ b/test/network/CMakeLists.txt @@ -1,20 +1,25 @@ -set(NETWORK_UNIT_TEST_FILES - network/TestNetworkManagerClient.cpp - network/TestNetworkManagerServer.cpp - PARENT_SCOPE - ) - -set(NETWORK_INTEGRATION_TEST_FILES - network/TestWin32WebServer.cpp - PARENT_SCOPE - ) - -set(NETWORK_UNIT_TEST_DEPENDENCIES - network - PARENT_SCOPE - ) - -set(NETWORK_INTEGRATION_TEST_DEPENDENCIES - network - PARENT_SCOPE - ) \ No newline at end of file +set(MODULE_NAME network) + +list(APPEND UNIT_TEST_FILES + TestNetworkManagerServer.cpp +) + +set(INTEGRATION_TEST_FILES) +if (WIN32) + list(APPEND INTEGRATION_TEST_FILES + TestWin32WebServer.cpp + TestWinsockClient.cpp + TestWinsockServer.cpp + ) +endif() + +set(UNIT_TEST_TARGET_NAME ${MODULE_NAME}_unit_tests) +set(INTEGRATION_TEST_TARGET_NAME ${MODULE_NAME}_integration_tests) + +add_executable(${UNIT_TEST_TARGET_NAME} ${CMAKE_SOURCE_DIR}/test/test_runner.cpp ${UNIT_TEST_FILES}) +target_link_libraries(${UNIT_TEST_TARGET_NAME} PUBLIC test_utils network) +set_property(TARGET ${UNIT_TEST_TARGET_NAME} PROPERTY FOLDER test/${MODULE_NAME}) + +add_executable(${INTEGRATION_TEST_TARGET_NAME} ${CMAKE_SOURCE_DIR}/test/test_runner.cpp ${INTEGRATION_TEST_FILES}) +target_link_libraries(${INTEGRATION_TEST_TARGET_NAME} PUBLIC test_utils network) +set_property(TARGET ${INTEGRATION_TEST_TARGET_NAME} PROPERTY FOLDER test/${MODULE_NAME}) diff --git a/test/network/TestNetworkManagerClient.cpp b/test/network/TestNetworkManagerClient.cpp deleted file mode 100644 index d8a4e7f..0000000 --- a/test/network/TestNetworkManagerClient.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "NetworkManager.h" - -#include "TestFramework.h" - -#include - -TEST_CASE(TestNetworkManagerClient, "network") -{ - auto network_manager = NetworkManager::Create(); - - network_manager->runHttpClient(); -} diff --git a/test/network/TestWinsockClient.cpp b/test/network/TestWinsockClient.cpp new file mode 100644 index 0000000..dfac299 --- /dev/null +++ b/test/network/TestWinsockClient.cpp @@ -0,0 +1,25 @@ +#include "NetworkManager.h" + +#include "TestFramework.h" +#include "TestUtils.h" + +#include "HttpClient.h" +#include "File.h" + +TEST_CASE(TestWinsockClient, "network") +{ + HttpClient client; + + HttpRequest request(HttpRequest::Verb::GET); + + HttpClient::Address address; + address.mHost = "127.0.0.1"; + address.mPort = 8000; + + auto response = client.makeRequest(request, address); + + auto content = response.toString(); + + File file(TestUtils::getTestOutputDir(__FILE__) / "get_request.dat"); + file.writeText(content); +} diff --git a/test/network/TestWinsockServer.cpp b/test/network/TestWinsockServer.cpp new file mode 100644 index 0000000..d169c68 --- /dev/null +++ b/test/network/TestWinsockServer.cpp @@ -0,0 +1,21 @@ +#include "NetworkManager.h" + +#include "TestFramework.h" +#include "TestUtils.h" + +#include "HttpServer.h" +#include "BasicWebApp.h" +#include "File.h" + +TEST_CASE(TestWinsockServer, "network") +{ + BasicWebApp app; + + HttpServer server; + + HttpServer::Address address; + address.mHost = "127.0.0.1"; + address.mPort = 8000; + + server.run(&app, address); +}