diff options
| author | Stefan Boberg <[email protected]> | 2023-10-13 14:46:49 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-10-13 14:46:49 +0200 |
| commit | c3fad0e98576ff5dee3ee63725459d46e201fa34 (patch) | |
| tree | 91455786fac76ffb6a83ff24620329780ce08545 /src | |
| parent | improved http.sys initialization diagnostics and amended logic for dedicated ... (diff) | |
| download | zen-c3fad0e98576ff5dee3ee63725459d46e201fa34.tar.xz zen-c3fad0e98576ff5dee3ee63725459d46e201fa34.zip | |
support for multiple http servers (#473)
* added support for having multiple http servers active in one session
* added configuration API to pluggable transports
* removed pimpl pattern from some pluggable transports implementations
Diffstat (limited to 'src')
| -rw-r--r-- | src/transports/transport-sdk/include/transportplugin.h | 21 | ||||
| -rw-r--r-- | src/transports/winsock/winsock.cpp | 85 | ||||
| -rw-r--r-- | src/transports/winsock/xmake.lua | 1 | ||||
| -rw-r--r-- | src/zenhttp/httpserver.cpp | 97 | ||||
| -rw-r--r-- | src/zenhttp/include/zenhttp/httpplugin.h | 25 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpmulti.cpp | 115 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpmulti.h | 32 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpplugin.cpp | 233 | ||||
| -rw-r--r-- | src/zenhttp/transports/asiotransport.cpp | 38 | ||||
| -rw-r--r-- | src/zenhttp/transports/asiotransport.h | 2 | ||||
| -rw-r--r-- | src/zenhttp/transports/dlltransport.cpp | 115 | ||||
| -rw-r--r-- | src/zenhttp/transports/dlltransport.h | 23 | ||||
| -rw-r--r-- | src/zenhttp/transports/winsocktransport.cpp | 163 | ||||
| -rw-r--r-- | src/zenhttp/transports/winsocktransport.h | 2 |
14 files changed, 559 insertions, 393 deletions
diff --git a/src/transports/transport-sdk/include/transportplugin.h b/src/transports/transport-sdk/include/transportplugin.h index aee5b2e7a..2a3b8075f 100644 --- a/src/transports/transport-sdk/include/transportplugin.h +++ b/src/transports/transport-sdk/include/transportplugin.h @@ -4,8 +4,16 @@ #include <stdint.h> -// Important note: this header is meant to compile standalone -// and should therefore not depend on anything from the Zen codebase +// This header is meant to compile standalone and should therefore NOT depend +// on anything from the Zen tree + +////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: Any development or changes to this header should be made +// in the Zen repository http://github.com/epicgames/zen even if you +// may find the header in the UE tree +// +////////////////////////////////////////////////////////////////////////// namespace zen { @@ -69,10 +77,11 @@ public: class TransportPlugin { public: - virtual uint32_t AddRef() const = 0; - virtual uint32_t Release() const = 0; - virtual void Initialize(TransportServer* ServerInterface) = 0; - virtual void Shutdown() = 0; + virtual uint32_t AddRef() const = 0; + virtual uint32_t Release() const = 0; + virtual void Configure(const char* OptionTag, const char* OptionValue) = 0; + virtual void Initialize(TransportServer* ServerInterface) = 0; + virtual void Shutdown() = 0; /** Check whether this transport is usable. */ diff --git a/src/transports/winsock/winsock.cpp b/src/transports/winsock/winsock.cpp index 25e46b2fb..7b349361e 100644 --- a/src/transports/winsock/winsock.cpp +++ b/src/transports/winsock/winsock.cpp @@ -2,9 +2,11 @@ #include <inttypes.h> #include <atomic> +#include <charconv> #include <exception> #include <future> #include <memory> +#include <optional> #include <thread> #include <zencore/refcount.h> @@ -24,18 +26,34 @@ ZEN_THIRD_PARTY_INCLUDES_END ////////////////////////////////////////////////////////////////////////// +template<Integral T> +std::optional<T> +ParseInt(const std::string_view& Input) +{ + T Out = 0; + const std::from_chars_result Result = std::from_chars(Input.data(), Input.data() + Input.size(), Out); + if (Result.ec == std::errc::invalid_argument || Result.ec == std::errc::result_out_of_range) + { + return std::nullopt; + } + return Out; +} + +////////////////////////////////////////////////////////////////////////// + using namespace zen; -class SocketTransportPlugin : public TransportPlugin, zen::RefCounted +class WinsockTransportPlugin : public TransportPlugin, zen::RefCounted { public: - SocketTransportPlugin(uint16_t BasePort, unsigned int ThreadCount); - ~SocketTransportPlugin(); + WinsockTransportPlugin(); + ~WinsockTransportPlugin(); // TransportPlugin implementation virtual uint32_t AddRef() const override; virtual uint32_t Release() const override; + virtual void Configure(const char* OptionTag, const char* OptionValue) override; virtual void Initialize(TransportServer* ServerInterface) override; virtual void Shutdown() override; virtual bool IsAvailable() override; @@ -44,7 +62,6 @@ private: TransportServer* m_ServerInterface = nullptr; bool m_IsOk = true; uint16_t m_BasePort = 0; - int m_ThreadCount = 0; SOCKET m_ListenSocket{}; std::thread m_AcceptThread; @@ -52,11 +69,11 @@ private: std::vector<std::future<void>> m_Connections; }; -struct SocketTransportConnection : public TransportConnection +struct WinsockTransportConnection : public TransportConnection { public: - SocketTransportConnection(); - ~SocketTransportConnection(); + WinsockTransportConnection(); + ~WinsockTransportConnection(); void Initialize(TransportServerConnection* ServerConnection, SOCKET ClientSocket); void HandleConnection(); @@ -75,16 +92,16 @@ private: ////////////////////////////////////////////////////////////////////////// -SocketTransportConnection::SocketTransportConnection() +WinsockTransportConnection::WinsockTransportConnection() { } -SocketTransportConnection::~SocketTransportConnection() +WinsockTransportConnection::~WinsockTransportConnection() { } void -SocketTransportConnection::Initialize(TransportServerConnection* ServerConnection, SOCKET ClientSocket) +WinsockTransportConnection::Initialize(TransportServerConnection* ServerConnection, SOCKET ClientSocket) { // ZEN_ASSERT(!m_ConnectionHandler); @@ -93,7 +110,7 @@ SocketTransportConnection::Initialize(TransportServerConnection* ServerConnectio } void -SocketTransportConnection::HandleConnection() +WinsockTransportConnection::HandleConnection() { // ZEN_ASSERT(m_ConnectionHandler); @@ -120,7 +137,7 @@ SocketTransportConnection::HandleConnection() } void -SocketTransportConnection::CloseConnection() +WinsockTransportConnection::CloseConnection() { if (m_IsTerminated) { @@ -137,7 +154,7 @@ SocketTransportConnection::CloseConnection() } int64_t -SocketTransportConnection::WriteBytes(const void* Buffer, size_t DataSize) +WinsockTransportConnection::WriteBytes(const void* Buffer, size_t DataSize) { const uint8_t* BufferCursor = reinterpret_cast<const uint8_t*>(Buffer); int64_t TotalBytesSent = 0; @@ -163,7 +180,7 @@ SocketTransportConnection::WriteBytes(const void* Buffer, size_t DataSize) } void -SocketTransportConnection::Shutdown(bool Receive, bool Transmit) +WinsockTransportConnection::Shutdown(bool Receive, bool Transmit) { if (Receive) { @@ -184,9 +201,7 @@ SocketTransportConnection::Shutdown(bool Receive, bool Transmit) ////////////////////////////////////////////////////////////////////////// -SocketTransportPlugin::SocketTransportPlugin(uint16_t BasePort, unsigned int ThreadCount) -: m_BasePort(BasePort) -, m_ThreadCount(ThreadCount != 0 ? ThreadCount : std::max(std::thread::hardware_concurrency(), 8u)) +WinsockTransportPlugin::WinsockTransportPlugin() { #if ZEN_PLATFORM_WINDOWS WSADATA wsaData; @@ -198,7 +213,7 @@ SocketTransportPlugin::SocketTransportPlugin(uint16_t BasePort, unsigned int Thr #endif } -SocketTransportPlugin::~SocketTransportPlugin() +WinsockTransportPlugin::~WinsockTransportPlugin() { Shutdown(); @@ -211,19 +226,37 @@ SocketTransportPlugin::~SocketTransportPlugin() } uint32_t -SocketTransportPlugin::AddRef() const +WinsockTransportPlugin::AddRef() const { return RefCounted::AddRef(); } uint32_t -SocketTransportPlugin::Release() const +WinsockTransportPlugin::Release() const { return RefCounted::Release(); } void -SocketTransportPlugin::Initialize(TransportServer* ServerInterface) +WinsockTransportPlugin::Configure(const char* OptionTag, const char* OptionValue) +{ + using namespace std::literals; + + if (OptionTag == "port"sv) + { + if (auto PortNum = ParseInt<uint16_t>(OptionValue)) + { + m_BasePort = *PortNum; + } + } + else + { + // Unknown configuration option + } +} + +void +WinsockTransportPlugin::Initialize(TransportServer* ServerInterface) { uint16_t Port = m_BasePort; @@ -267,8 +300,8 @@ SocketTransportPlugin::Initialize(TransportServer* ServerInterface) setsockopt(ClientSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&Flag, sizeof(Flag)); // Handle new connection - SocketTransportConnection* Connection = new SocketTransportConnection(); - TransportServerConnection* ConnectionInterface{m_ServerInterface->CreateConnectionHandler(Connection)}; + WinsockTransportConnection* Connection = new WinsockTransportConnection(); + TransportServerConnection* ConnectionInterface{m_ServerInterface->CreateConnectionHandler(Connection)}; Connection->Initialize(ConnectionInterface, ClientSocket); m_Connections.push_back(std::async(std::launch::async, [Connection] { @@ -294,7 +327,7 @@ SocketTransportPlugin::Initialize(TransportServer* ServerInterface) } void -SocketTransportPlugin::Shutdown() +WinsockTransportPlugin::Shutdown() { // TODO: all pending/ongoing work should be drained here as well @@ -310,7 +343,7 @@ SocketTransportPlugin::Shutdown() } bool -SocketTransportPlugin::IsAvailable() +WinsockTransportPlugin::IsAvailable() { return true; } @@ -320,7 +353,7 @@ SocketTransportPlugin::IsAvailable() TransportPlugin* CreateTransportPlugin() { - return new SocketTransportPlugin(8558, 8); + return new WinsockTransportPlugin; } BOOL WINAPI diff --git a/src/transports/winsock/xmake.lua b/src/transports/winsock/xmake.lua index 9f9a32daf..781ead179 100644 --- a/src/transports/winsock/xmake.lua +++ b/src/transports/winsock/xmake.lua @@ -2,6 +2,7 @@ target("winsock") set_kind("shared") + set_group("transports") add_headerfiles("**.h") add_files("**.cpp") add_links("Ws2_32") diff --git a/src/zenhttp/httpserver.cpp b/src/zenhttp/httpserver.cpp index cd62ea157..aa8bdddd6 100644 --- a/src/zenhttp/httpserver.cpp +++ b/src/zenhttp/httpserver.cpp @@ -3,6 +3,7 @@ #include <zenhttp/httpserver.h> #include "servers/httpasio.h" +#include "servers/httpmulti.h" #include "servers/httpnull.h" #include "servers/httpsys.h" #include "zenhttp/httpplugin.h" @@ -719,39 +720,13 @@ enum class HttpServerClass kHttpAsio, kHttpSys, kHttpPlugin, + kHttpMulti, kHttpNull }; Ref<HttpServer> -CreateHttpServer(const HttpServerConfig& Config) +CreateHttpServerClass(HttpServerClass Class, const HttpServerConfig& Config) { - using namespace std::literals; - - HttpServerClass Class = HttpServerClass::kHttpNull; - -#if ZEN_WITH_HTTPSYS - Class = HttpServerClass::kHttpSys; -#else - Class = HttpServerClass::kHttpAsio; -#endif - - if (Config.ServerClass == "asio"sv) - { - Class = HttpServerClass::kHttpAsio; - } - else if (Config.ServerClass == "httpsys"sv) - { - Class = HttpServerClass::kHttpSys; - } - else if (Config.ServerClass == "plugin"sv) - { - Class = HttpServerClass::kHttpPlugin; - } - else if (Config.ServerClass == "null"sv) - { - Class = HttpServerClass::kHttpNull; - } - switch (Class) { default: @@ -759,25 +734,42 @@ CreateHttpServer(const HttpServerConfig& Config) ZEN_INFO("using asio HTTP server implementation"); return CreateHttpAsioServer(Config.ThreadCount); + case HttpServerClass::kHttpMulti: + { + ZEN_INFO("using multi HTTP server implementation"); + Ref<HttpMultiServer> Server{new HttpMultiServer()}; + + // This is hardcoded for now, but should be configurable in the future + Server->AddServer(CreateHttpServerClass(HttpServerClass::kHttpSys, Config)); + Server->AddServer(CreateHttpServerClass(HttpServerClass::kHttpPlugin, Config)); + + return Server; + } + #if ZEN_WITH_PLUGINS case HttpServerClass::kHttpPlugin: { ZEN_INFO("using plugin HTTP server implementation"); - Ref<HttpPluginServer> Server{new HttpPluginServer(Config.ThreadCount)}; + Ref<HttpPluginServer> Server{CreateHttpPluginServer()}; -# if 1 - Ref<TransportPlugin> WinsockPlugin{CreateSocketTransportPlugin(8558, Config.ThreadCount)}; + // This is hardcoded for now, but should be configurable in the future + +# if 0 + Ref<TransportPlugin> WinsockPlugin{CreateSocketTransportPlugin()}; + WinsockPlugin->Configure("port", "8055"); Server->AddPlugin(WinsockPlugin); # endif # if 0 - Ref<TransportPlugin> AsioPlugin{CreateAsioTransportPlugin(8558, Config.ThreadCount)}; + Ref<TransportPlugin> AsioPlugin{CreateAsioTransportPlugin()}; + AsioPlugin->Configure("port", "8055"); Server->AddPlugin(AsioPlugin); # endif -# if 0 - Ref<DllTransportPlugin> DllPlugin{new DllTransportPlugin(8558, Config.ThreadCount)}; +# if 1 + Ref<DllTransportPlugin> DllPlugin{CreateDllTransportPlugin()}; DllPlugin->LoadDll("winsock"); + DllPlugin->ConfigureDll("winsock", "port", "8055"); Server->AddPlugin(DllPlugin); # endif @@ -801,6 +793,43 @@ CreateHttpServer(const HttpServerConfig& Config) } } +Ref<HttpServer> +CreateHttpServer(const HttpServerConfig& Config) +{ + using namespace std::literals; + + HttpServerClass Class = HttpServerClass::kHttpNull; + +#if ZEN_WITH_HTTPSYS + Class = HttpServerClass::kHttpSys; +#else + Class = HttpServerClass::kHttpAsio; +#endif + + if (Config.ServerClass == "asio"sv) + { + Class = HttpServerClass::kHttpAsio; + } + else if (Config.ServerClass == "httpsys"sv) + { + Class = HttpServerClass::kHttpSys; + } + else if (Config.ServerClass == "plugin"sv) + { + Class = HttpServerClass::kHttpPlugin; + } + else if (Config.ServerClass == "null"sv) + { + Class = HttpServerClass::kHttpNull; + } + else if (Config.ServerClass == "multi"sv) + { + Class = HttpServerClass::kHttpMulti; + } + + return CreateHttpServerClass(Class, Config); +} + ////////////////////////////////////////////////////////////////////////// bool diff --git a/src/zenhttp/include/zenhttp/httpplugin.h b/src/zenhttp/include/zenhttp/httpplugin.h index de54b9042..30a66e92e 100644 --- a/src/zenhttp/include/zenhttp/httpplugin.h +++ b/src/zenhttp/include/zenhttp/httpplugin.h @@ -3,7 +3,6 @@ #pragma once #include <zencore/refcount.h> -#include <zencore/thread.h> #if !defined(ZEN_WITH_PLUGINS) # if ZEN_PLATFORM_WINDOWS @@ -19,31 +18,15 @@ namespace zen { -struct HttpPluginServerImpl; - class HttpPluginServer : public HttpServer { public: - HttpPluginServer(unsigned int ThreadCount); - ~HttpPluginServer(); - - virtual void RegisterService(HttpService& Service) override; - virtual int Initialize(int BasePort) override; - virtual void Run(bool IsInteractiveSession) override; - virtual void RequestExit() override; - virtual void Close() override; - - void AddPlugin(Ref<TransportPlugin> Plugin); - void RemovePlugin(Ref<TransportPlugin> Plugin); - -private: - Event m_ShutdownEvent; - int m_BasePort = 0; - unsigned int m_ThreadCount = 0; - - HttpPluginServerImpl* m_Impl = nullptr; + virtual void AddPlugin(Ref<TransportPlugin> Plugin) = 0; + virtual void RemovePlugin(Ref<TransportPlugin> Plugin) = 0; }; +Ref<HttpPluginServer> CreateHttpPluginServer(); + } // namespace zen #endif diff --git a/src/zenhttp/servers/httpmulti.cpp b/src/zenhttp/servers/httpmulti.cpp new file mode 100644 index 000000000..6b7060d5e --- /dev/null +++ b/src/zenhttp/servers/httpmulti.cpp @@ -0,0 +1,115 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "httpmulti.h" + +#include <zencore/logging.h> + +#if ZEN_PLATFORM_WINDOWS +# include <conio.h> +#endif + +namespace zen { + +HttpMultiServer::HttpMultiServer() +{ +} + +HttpMultiServer::~HttpMultiServer() +{ +} + +void +HttpMultiServer::RegisterService(HttpService& Service) +{ + for (auto& Server : m_Servers) + { + Server->RegisterService(Service); + } +} + +int +HttpMultiServer::Initialize(int BasePort) +{ + ZEN_ASSERT(!m_IsInitialized); + + int EffectivePort = 0; + + for (auto& Server : m_Servers) + { + const int InitializeResult = Server->Initialize(BasePort); + + if (!EffectivePort) + { + EffectivePort = InitializeResult; + } + } + + m_IsInitialized = true; + + return EffectivePort; +} + +void +HttpMultiServer::Run(bool IsInteractiveSession) +{ + const bool TestMode = !IsInteractiveSession; + + int WaitTimeout = -1; + if (!TestMode) + { + WaitTimeout = 1000; + } + +#if ZEN_PLATFORM_WINDOWS + if (TestMode == false) + { + zen::logging::ConsoleLog().info("Zen Server running (multi server). Press ESC or Q to quit"); + } + + do + { + if (!TestMode && _kbhit() != 0) + { + char c = (char)_getch(); + + if (c == 27 || c == 'Q' || c == 'q') + { + RequestApplicationExit(0); + } + } + + m_ShutdownEvent.Wait(WaitTimeout); + } while (!IsApplicationExitRequested()); +#else + if (TestMode == false) + { + zen::logging::ConsoleLog().info("Zen Server running (null HTTP). Ctrl-C to quit"); + } + + do + { + m_ShutdownEvent.Wait(WaitTimeout); + } while (!IsApplicationExitRequested()); +#endif +} + +void +HttpMultiServer::RequestExit() +{ + m_ShutdownEvent.Set(); +} + +void +HttpMultiServer::Close() +{ +} + +void +HttpMultiServer::AddServer(Ref<HttpServer> Server) +{ + ZEN_ASSERT(!m_IsInitialized); + + m_Servers.push_back(std::move(Server)); +} + +} // namespace zen diff --git a/src/zenhttp/servers/httpmulti.h b/src/zenhttp/servers/httpmulti.h new file mode 100644 index 000000000..d5b21d3c3 --- /dev/null +++ b/src/zenhttp/servers/httpmulti.h @@ -0,0 +1,32 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <zencore/thread.h> +#include <zenhttp/httpserver.h> + +#include <memory> + +namespace zen { + +class HttpMultiServer : public HttpServer +{ +public: + HttpMultiServer(); + ~HttpMultiServer(); + + virtual void RegisterService(HttpService& Service) override; + virtual int Initialize(int BasePort) override; + virtual void Run(bool IsInteractiveSession) override; + virtual void RequestExit() override; + virtual void Close() override; + + void AddServer(Ref<HttpServer> Server); + +private: + bool m_IsInitialized = false; + Event m_ShutdownEvent; + std::vector<Ref<HttpServer>> m_Servers; +}; + +} // namespace zen diff --git a/src/zenhttp/servers/httpplugin.cpp b/src/zenhttp/servers/httpplugin.cpp index 2e934473e..3c727763b 100644 --- a/src/zenhttp/servers/httpplugin.cpp +++ b/src/zenhttp/servers/httpplugin.cpp @@ -8,8 +8,8 @@ # include <zencore/except.h> # include <zencore/logging.h> +# include <zencore/thread.h> # include <zencore/trace.h> -# include <zencore/workthreadpool.h> # include <zenhttp/httpserver.h> # include <memory> @@ -74,18 +74,22 @@ private: ////////////////////////////////////////////////////////////////////////// -struct HttpPluginServerImpl : public TransportServer +struct HttpPluginServerImpl : public HttpPluginServer, TransportServer { HttpPluginServerImpl(); ~HttpPluginServerImpl(); - void AddPlugin(Ref<TransportPlugin> Plugin); - void RemovePlugin(Ref<TransportPlugin> Plugin); + // HttpPluginServer - void Start(); - void Stop(); + virtual void RegisterService(HttpService& Service) override; + virtual int Initialize(int BasePort) override; + virtual void Run(bool IsInteractiveSession) override; + virtual void RequestExit() override; + virtual void Close() override; + + virtual void AddPlugin(Ref<TransportPlugin> Plugin) override; + virtual void RemovePlugin(Ref<TransportPlugin> Plugin) override; - void RegisterService(const char* InUrlPath, HttpService& Service); HttpService* RouteRequest(std::string_view Url); struct ServiceEntry @@ -94,9 +98,11 @@ struct HttpPluginServerImpl : public TransportServer HttpService* Service; }; + bool m_IsInitialized = false; RwLock m_Lock; std::vector<ServiceEntry> m_UriHandlers; std::vector<Ref<TransportPlugin>> m_Plugins; + Event m_ShutdownEvent; // TransportServer @@ -568,149 +574,71 @@ HttpPluginServerImpl::CreateConnectionHandler(TransportConnection* Connection) return Handler; } -void -HttpPluginServerImpl::Start() +int +HttpPluginServerImpl::Initialize(int BasePort) { - RwLock::ExclusiveLockScope _(m_Lock); - - for (auto& Plugin : m_Plugins) + try { - try - { - Plugin->Initialize(this); - } - catch (std::exception& Ex) + RwLock::ExclusiveLockScope _(m_Lock); + + for (auto& Plugin : m_Plugins) { - ZEN_WARN("exception caught during plugin initialization: {}", Ex.what()); + try + { + Plugin->Initialize(this); + } + catch (std::exception& Ex) + { + ZEN_WARN("exception caught during plugin initialization: {}", Ex.what()); + } } } -} - -void -HttpPluginServerImpl::Stop() -{ - RwLock::ExclusiveLockScope _(m_Lock); - - for (auto& Plugin : m_Plugins) + catch (std::exception& ex) { - try - { - Plugin->Shutdown(); - } - catch (std::exception& Ex) - { - ZEN_WARN("exception caught during plugin shutdown: {}", Ex.what()); - } - - Plugin = nullptr; + ZEN_WARN("Caught exception starting http plugin server: {}", ex.what()); } - m_Plugins.clear(); -} + m_IsInitialized = true; -void -HttpPluginServerImpl::AddPlugin(Ref<TransportPlugin> Plugin) -{ - RwLock::ExclusiveLockScope _(m_Lock); - m_Plugins.emplace_back(std::move(Plugin)); + return BasePort; } void -HttpPluginServerImpl::RemovePlugin(Ref<TransportPlugin> Plugin) +HttpPluginServerImpl::Close() { - RwLock::ExclusiveLockScope _(m_Lock); - auto It = std::find(begin(m_Plugins), end(m_Plugins), Plugin); - if (It != m_Plugins.end()) - { - m_Plugins.erase(It); - } -} + if (!m_IsInitialized) + return; -void -HttpPluginServerImpl::RegisterService(const char* InUrlPath, HttpService& Service) -{ - std::string_view UrlPath(InUrlPath); - Service.SetUriPrefixLength(UrlPath.size()); - if (!UrlPath.empty() && UrlPath.back() == '/') + try { - UrlPath.remove_suffix(1); - } - - RwLock::ExclusiveLockScope _(m_Lock); - m_UriHandlers.push_back({std::string(UrlPath), &Service}); -} - -HttpService* -HttpPluginServerImpl::RouteRequest(std::string_view Url) -{ - RwLock::SharedLockScope _(m_Lock); + RwLock::ExclusiveLockScope _(m_Lock); - HttpService* CandidateService = nullptr; - std::string::size_type CandidateMatchSize = 0; - for (const ServiceEntry& SvcEntry : m_UriHandlers) - { - const std::string& SvcUrl = SvcEntry.ServiceUrlPath; - const std::string::size_type SvcUrlSize = SvcUrl.size(); - if ((SvcUrlSize >= CandidateMatchSize) && Url.compare(0, SvcUrlSize, SvcUrl) == 0 && - ((SvcUrlSize == Url.size()) || (Url[SvcUrlSize] == '/'))) + for (auto& Plugin : m_Plugins) { - CandidateMatchSize = SvcUrl.size(); - CandidateService = SvcEntry.Service; - } - } - - return CandidateService; -} - -////////////////////////////////////////////////////////////////////////// - -HttpPluginServer::HttpPluginServer(unsigned int ThreadCount) -: m_ThreadCount(ThreadCount != 0 ? ThreadCount : Max(std::thread::hardware_concurrency(), 8u)) -, m_Impl(new HttpPluginServerImpl) -{ -} - -HttpPluginServer::~HttpPluginServer() -{ - if (m_Impl) - { - ZEN_ERROR("~HttpPluginServer() called without calling Close() first"); - } -} - -int -HttpPluginServer::Initialize(int BasePort) -{ - try - { - m_Impl->Start(); - } - catch (std::exception& ex) - { - ZEN_WARN("Caught exception starting http plugin server: {}", ex.what()); - } + try + { + Plugin->Shutdown(); + } + catch (std::exception& Ex) + { + ZEN_WARN("exception caught during plugin shutdown: {}", Ex.what()); + } - return BasePort; -} + Plugin = nullptr; + } -void -HttpPluginServer::Close() -{ - try - { - m_Impl->Stop(); + m_Plugins.clear(); } catch (std::exception& ex) { ZEN_WARN("Caught exception stopping http plugin server: {}", ex.what()); } - delete m_Impl; - m_Impl = nullptr; + m_IsInitialized = false; } void -HttpPluginServer::Run(bool IsInteractive) +HttpPluginServerImpl::Run(bool IsInteractive) { const bool TestMode = !IsInteractive; @@ -754,27 +682,74 @@ HttpPluginServer::Run(bool IsInteractive) } void -HttpPluginServer::RequestExit() +HttpPluginServerImpl::RequestExit() { m_ShutdownEvent.Set(); } void -HttpPluginServer::RegisterService(HttpService& Service) +HttpPluginServerImpl::AddPlugin(Ref<TransportPlugin> Plugin) { - m_Impl->RegisterService(Service.BaseUri(), Service); + RwLock::ExclusiveLockScope _(m_Lock); + m_Plugins.emplace_back(std::move(Plugin)); } void -HttpPluginServer::AddPlugin(Ref<TransportPlugin> Plugin) +HttpPluginServerImpl::RemovePlugin(Ref<TransportPlugin> Plugin) { - m_Impl->AddPlugin(Plugin); + RwLock::ExclusiveLockScope _(m_Lock); + auto It = std::find(begin(m_Plugins), end(m_Plugins), Plugin); + if (It != m_Plugins.end()) + { + m_Plugins.erase(It); + } } void -HttpPluginServer::RemovePlugin(Ref<TransportPlugin> Plugin) +HttpPluginServerImpl::RegisterService(HttpService& Service) +{ + std::string_view UrlPath(Service.BaseUri()); + Service.SetUriPrefixLength(UrlPath.size()); + + if (!UrlPath.empty() && UrlPath.back() == '/') + { + UrlPath.remove_suffix(1); + } + + RwLock::ExclusiveLockScope _(m_Lock); + m_UriHandlers.push_back({std::string(UrlPath), &Service}); +} + +HttpService* +HttpPluginServerImpl::RouteRequest(std::string_view Url) +{ + RwLock::SharedLockScope _(m_Lock); + + HttpService* CandidateService = nullptr; + std::string::size_type CandidateMatchSize = 0; + for (const ServiceEntry& SvcEntry : m_UriHandlers) + { + const std::string& SvcUrl = SvcEntry.ServiceUrlPath; + const std::string::size_type SvcUrlSize = SvcUrl.size(); + if ((SvcUrlSize >= CandidateMatchSize) && Url.compare(0, SvcUrlSize, SvcUrl) == 0 && + ((SvcUrlSize == Url.size()) || (Url[SvcUrlSize] == '/'))) + { + CandidateMatchSize = SvcUrl.size(); + CandidateService = SvcEntry.Service; + } + } + + return CandidateService; +} + +////////////////////////////////////////////////////////////////////////// + +struct HttpPluginServerImpl; + +Ref<HttpPluginServer> +CreateHttpPluginServer() { - m_Impl->RemovePlugin(Plugin); + return Ref<HttpPluginServer>(new HttpPluginServerImpl); } } // namespace zen diff --git a/src/zenhttp/transports/asiotransport.cpp b/src/zenhttp/transports/asiotransport.cpp index b8fef8f5f..ab053a748 100644 --- a/src/zenhttp/transports/asiotransport.cpp +++ b/src/zenhttp/transports/asiotransport.cpp @@ -31,18 +31,19 @@ struct AsioTransportAcceptor; class AsioTransportPlugin : public TransportPlugin, RefCounted { public: - AsioTransportPlugin(uint16_t BasePort, unsigned int ThreadCount); + AsioTransportPlugin(); ~AsioTransportPlugin(); virtual uint32_t AddRef() const override; virtual uint32_t Release() const override; + virtual void Configure(const char* OptionTag, const char* OptionValue) override; virtual void Initialize(TransportServer* ServerInterface) override; virtual void Shutdown() override; virtual bool IsAvailable() override; private: bool m_IsOk = true; - uint16_t m_BasePort = 0; + uint16_t m_BasePort = 8558; int m_ThreadCount = 0; asio::io_service m_IoService; @@ -349,9 +350,7 @@ AsioTransportConnection::OnResponseDataSent(const asio::error_code& Ec, std::siz ////////////////////////////////////////////////////////////////////////// -AsioTransportPlugin::AsioTransportPlugin(uint16_t BasePort, unsigned int ThreadCount) -: m_BasePort(BasePort) -, m_ThreadCount(ThreadCount != 0 ? ThreadCount : Max(std::thread::hardware_concurrency(), 8u)) +AsioTransportPlugin::AsioTransportPlugin() : m_ThreadCount(Max(std::thread::hardware_concurrency(), 8u)) { } @@ -372,6 +371,31 @@ AsioTransportPlugin::Release() const } void +AsioTransportPlugin::Configure(const char* OptionTag, const char* OptionValue) +{ + using namespace std::literals; + + if (OptionTag == "port"sv) + { + if (auto PortNum = ParseInt<uint16_t>(OptionValue)) + { + m_BasePort = *PortNum; + } + } + else if (OptionTag == "threads"sv) + { + if (auto ThreadCount = ParseInt<int>(OptionValue)) + { + m_ThreadCount = *ThreadCount; + } + } + else + { + // Unknown configuration option + } +} + +void AsioTransportPlugin::Initialize(TransportServer* ServerInterface) { ZEN_ASSERT(m_ThreadCount > 0); @@ -429,9 +453,9 @@ AsioTransportPlugin::IsAvailable() } TransportPlugin* -CreateAsioTransportPlugin(uint16_t BasePort, unsigned int ThreadCount) +CreateAsioTransportPlugin() { - return new AsioTransportPlugin(BasePort, ThreadCount); + return new AsioTransportPlugin(); } } // namespace zen diff --git a/src/zenhttp/transports/asiotransport.h b/src/zenhttp/transports/asiotransport.h index b10174b85..25f122b5b 100644 --- a/src/zenhttp/transports/asiotransport.h +++ b/src/zenhttp/transports/asiotransport.h @@ -8,7 +8,7 @@ namespace zen { -TransportPlugin* CreateAsioTransportPlugin(uint16_t BasePort, unsigned int ThreadCount); +TransportPlugin* CreateAsioTransportPlugin(); } // namespace zen diff --git a/src/zenhttp/transports/dlltransport.cpp b/src/zenhttp/transports/dlltransport.cpp index 04fb6caaa..5c0084e44 100644 --- a/src/zenhttp/transports/dlltransport.cpp +++ b/src/zenhttp/transports/dlltransport.cpp @@ -86,28 +86,29 @@ struct LoadedDll Ref<TransportPlugin> Plugin; }; -class DllTransportPluginImpl +class DllTransportPluginImpl : public DllTransportPlugin, RefCounted { public: - DllTransportPluginImpl(uint16_t BasePort, unsigned int ThreadCount); + DllTransportPluginImpl(); ~DllTransportPluginImpl(); - uint16_t Start(TransportServer* ServerInterface); - void Stop(); - bool IsAvailable(); - void LoadDll(std::string_view Name); + virtual uint32_t AddRef() const override; + virtual uint32_t Release() const override; + virtual void Configure(const char* OptionTag, const char* OptionValue) override; + virtual void Initialize(TransportServer* ServerInterface) override; + virtual void Shutdown() override; + virtual bool IsAvailable() override; + + virtual void LoadDll(std::string_view Name) override; + virtual void ConfigureDll(std::string_view Name, const char* OptionTag, const char* OptionValue) override; private: TransportServer* m_ServerInterface = nullptr; RwLock m_Lock; std::vector<LoadedDll> m_Transports; - uint16_t m_BasePort = 0; - int m_ThreadCount = 0; }; -DllTransportPluginImpl::DllTransportPluginImpl(uint16_t BasePort, unsigned int ThreadCount) -: m_BasePort(BasePort) -, m_ThreadCount(ThreadCount != 0 ? ThreadCount : Max(std::thread::hardware_concurrency(), 8u)) +DllTransportPluginImpl::DllTransportPluginImpl() { } @@ -115,8 +116,28 @@ DllTransportPluginImpl::~DllTransportPluginImpl() { } -uint16_t -DllTransportPluginImpl::Start(TransportServer* ServerIface) +uint32_t +DllTransportPluginImpl::AddRef() const +{ + return RefCounted::AddRef(); +} + +uint32_t +DllTransportPluginImpl::Release() const +{ + return RefCounted::Release(); +} + +void +DllTransportPluginImpl::Configure(const char* OptionTag, const char* OptionValue) +{ + // No configuration options + + ZEN_UNUSED(OptionTag, OptionValue); +} + +void +DllTransportPluginImpl::Initialize(TransportServer* ServerIface) { m_ServerInterface = ServerIface; @@ -133,12 +154,10 @@ DllTransportPluginImpl::Start(TransportServer* ServerIface) // TODO: report } } - - return m_BasePort; } void -DllTransportPluginImpl::Stop() +DllTransportPluginImpl::Shutdown() { RwLock::ExclusiveLockScope _(m_Lock); @@ -162,8 +181,24 @@ DllTransportPluginImpl::IsAvailable() } void +DllTransportPluginImpl::ConfigureDll(std::string_view Name, const char* OptionTag, const char* OptionValue) +{ + RwLock::ExclusiveLockScope _(m_Lock); + + for (auto& Transport : m_Transports) + { + if (Transport.Name == Name) + { + Transport.Plugin->Configure(OptionTag, OptionValue); + } + } +} + +void DllTransportPluginImpl::LoadDll(std::string_view Name) { + RwLock::ExclusiveLockScope _(m_Lock); + ExtendableStringBuilder<128> DllPath; DllPath << Name << ".dll"; HMODULE DllHandle = LoadLibraryA(DllPath.c_str()); @@ -197,52 +232,10 @@ DllTransportPluginImpl::LoadDll(std::string_view Name) m_Transports.emplace_back(std::move(NewDll)); } -////////////////////////////////////////////////////////////////////////// - -DllTransportPlugin::DllTransportPlugin(uint16_t BasePort, unsigned int ThreadCount) -: m_Impl(std::make_unique<DllTransportPluginImpl>(BasePort, ThreadCount)) -{ -} - -DllTransportPlugin::~DllTransportPlugin() -{ - m_Impl->Stop(); -} - -uint32_t -DllTransportPlugin::AddRef() const -{ - return RefCounted::AddRef(); -} - -uint32_t -DllTransportPlugin::Release() const -{ - return RefCounted::Release(); -} - -void -DllTransportPlugin::Initialize(TransportServer* ServerInterface) -{ - m_Impl->Start(ServerInterface); -} - -void -DllTransportPlugin::Shutdown() -{ - m_Impl->Stop(); -} - -bool -DllTransportPlugin::IsAvailable() -{ - return m_Impl->IsAvailable(); -} - -void -DllTransportPlugin::LoadDll(std::string_view Name) +DllTransportPlugin* +CreateDllTransportPlugin() { - return m_Impl->LoadDll(Name); + return new DllTransportPluginImpl; } } // namespace zen diff --git a/src/zenhttp/transports/dlltransport.h b/src/zenhttp/transports/dlltransport.h index 2dccdd0f9..9346a10ce 100644 --- a/src/zenhttp/transports/dlltransport.h +++ b/src/zenhttp/transports/dlltransport.h @@ -8,30 +8,19 @@ namespace zen { -class DllTransportPluginImpl; - /** Transport plugin which supports dynamic loading of external transport * provider modules */ -class DllTransportPlugin : public TransportPlugin, RefCounted + +class DllTransportPlugin : public TransportPlugin { public: - DllTransportPlugin(uint16_t BasePort, unsigned int ThreadCount); - ~DllTransportPlugin(); - - virtual uint32_t AddRef() const override; - virtual uint32_t Release() const override; - - virtual void Initialize(TransportServer* ServerInterface) override; - virtual void Shutdown() override; - virtual bool IsAvailable() override; - - void LoadDll(std::string_view Name); - -private: - std::unique_ptr<DllTransportPluginImpl> m_Impl; + virtual void LoadDll(std::string_view Name) = 0; + virtual void ConfigureDll(std::string_view Name, const char* OptionTag, const char* OptionValue) = 0; }; +DllTransportPlugin* CreateDllTransportPlugin(); + } // namespace zen #endif diff --git a/src/zenhttp/transports/winsocktransport.cpp b/src/zenhttp/transports/winsocktransport.cpp index ad3302550..2397dd7cf 100644 --- a/src/zenhttp/transports/winsocktransport.cpp +++ b/src/zenhttp/transports/winsocktransport.cpp @@ -20,27 +20,6 @@ ZEN_THIRD_PARTY_INCLUDES_END namespace zen { -class SocketTransportPluginImpl; - -class SocketTransportPlugin : public TransportPlugin, RefCounted -{ -public: - SocketTransportPlugin(uint16_t BasePort, unsigned int ThreadCount); - ~SocketTransportPlugin(); - - virtual uint32_t AddRef() const override; - virtual uint32_t Release() const override; - virtual void Initialize(TransportServer* ServerInterface) override; - virtual void Shutdown() override; - virtual bool IsAvailable() override; - -private: - bool m_IsOk = true; - uint16_t m_BasePort = 0; - int m_ThreadCount = 0; - SocketTransportPluginImpl* m_Impl; -}; - struct SocketTransportConnection : public TransportConnection { public: @@ -172,19 +151,23 @@ SocketTransportConnection::Shutdown(bool Receive, bool Transmit) ////////////////////////////////////////////////////////////////////////// -class SocketTransportPluginImpl +class SocketTransportPluginImpl : public TransportPlugin, RefCounted { public: - SocketTransportPluginImpl(uint16_t BasePort, unsigned int ThreadCount); + SocketTransportPluginImpl(); ~SocketTransportPluginImpl(); - uint16_t Start(uint16_t Port, TransportServer* ServerInterface); - void Stop(); + virtual uint32_t AddRef() const override; + virtual uint32_t Release() const override; + virtual void Configure(const char* OptionTag, const char* OptionValue) override; + virtual void Initialize(TransportServer* ServerInterface) override; + virtual void Shutdown() override; + virtual bool IsAvailable() override; private: TransportServer* m_ServerInterface = nullptr; - uint16_t m_BasePort = 0; - int m_ThreadCount = 0; + uint16_t m_BasePort = 8558; + int m_ThreadCount = 8; bool m_IsOk = true; SOCKET m_ListenSocket{}; @@ -193,9 +176,7 @@ private: std::unique_ptr<WorkerThreadPool> m_WorkerThreadpool; }; -SocketTransportPluginImpl::SocketTransportPluginImpl(uint16_t BasePort, unsigned int ThreadCount) -: m_BasePort(BasePort) -, m_ThreadCount(ThreadCount) +SocketTransportPluginImpl::SocketTransportPluginImpl() { # if ZEN_PLATFORM_WINDOWS WSADATA wsaData; @@ -205,13 +186,11 @@ SocketTransportPluginImpl::SocketTransportPluginImpl(uint16_t BasePort, unsigned WSACleanup(); } # endif - - m_WorkerThreadpool = std::make_unique<WorkerThreadPool>(m_ThreadCount, "http_conn"); } SocketTransportPluginImpl::~SocketTransportPluginImpl() { - Stop(); + Shutdown(); # if ZEN_PLATFORM_WINDOWS if (m_IsOk) @@ -221,36 +200,81 @@ SocketTransportPluginImpl::~SocketTransportPluginImpl() # endif } -uint16_t -SocketTransportPluginImpl::Start(uint16_t Port, TransportServer* ServerInterface) +uint32_t +SocketTransportPluginImpl::AddRef() const +{ + return RefCounted::AddRef(); +} + +uint32_t +SocketTransportPluginImpl::Release() const +{ + return RefCounted::Release(); +} + +void +SocketTransportPluginImpl::Configure(const char* OptionTag, const char* OptionValue) +{ + using namespace std::literals; + + if (OptionTag == "port"sv) + { + if (auto PortNum = ParseInt<uint16_t>(OptionValue)) + { + m_BasePort = *PortNum; + } + } + else if (OptionTag == "threads"sv) + { + if (auto ThreadCount = ParseInt<int>(OptionValue)) + { + m_ThreadCount = *ThreadCount; + } + } + else + { + // Unknown configuration option + } +} + +bool +SocketTransportPluginImpl::IsAvailable() { - m_ServerInterface = ServerInterface; - m_ListenSocket = socket(AF_INET6, SOCK_STREAM, 0); + return true; +} + +void +SocketTransportPluginImpl::Initialize(TransportServer* ServerInterface) +{ + m_ServerInterface = ServerInterface; + m_WorkerThreadpool = std::make_unique<WorkerThreadPool>(m_ThreadCount, "http_conn"); + + m_ListenSocket = socket(AF_INET6, SOCK_STREAM, 0); if (m_ListenSocket == SOCKET_ERROR || m_ListenSocket == INVALID_SOCKET) { ZEN_ERROR("socket creation failed in HTTP plugin server init: {}", WSAGetLastError()); - return 0; + return; } sockaddr_in6 Server{}; Server.sin6_family = AF_INET6; - Server.sin6_port = htons(Port); + Server.sin6_port = htons(m_BasePort); Server.sin6_addr = in6addr_any; if (int Result = bind(m_ListenSocket, (sockaddr*)&Server, sizeof(Server)); Result == SOCKET_ERROR) { ZEN_ERROR("bind call failed in HTTP plugin server init: {}", WSAGetLastError()); - return 0; + return; } if (int Result = listen(m_ListenSocket, AF_INET6); Result == SOCKET_ERROR) { ZEN_ERROR("listen call failed in HTTP plugin server init: {}", WSAGetLastError()); - return 0; + return; } m_KeepRunning.test_and_set(); @@ -292,19 +316,20 @@ SocketTransportPluginImpl::Start(uint16_t Port, TransportServer* ServerInterface ZEN_INFO("HTTP plugin server accept thread exit"); }); - - return Port; } void -SocketTransportPluginImpl::Stop() +SocketTransportPluginImpl::Shutdown() { // TODO: all pending/ongoing work should be drained here as well m_KeepRunning.clear(); - closesocket(m_ListenSocket); - m_ListenSocket = 0; + if (m_ListenSocket) + { + closesocket(m_ListenSocket); + m_ListenSocket = 0; + } if (m_AcceptThread.joinable()) { @@ -314,52 +339,10 @@ SocketTransportPluginImpl::Stop() ////////////////////////////////////////////////////////////////////////// -SocketTransportPlugin::SocketTransportPlugin(uint16_t BasePort, unsigned int ThreadCount) -: m_BasePort(BasePort) -, m_ThreadCount(ThreadCount != 0 ? ThreadCount : Max(std::thread::hardware_concurrency(), 8u)) -, m_Impl(new SocketTransportPluginImpl(BasePort, m_ThreadCount)) -{ -} - -SocketTransportPlugin::~SocketTransportPlugin() -{ - delete m_Impl; -} - -uint32_t -SocketTransportPlugin::AddRef() const -{ - return RefCounted::AddRef(); -} - -uint32_t -SocketTransportPlugin::Release() const -{ - return RefCounted::Release(); -} - -void -SocketTransportPlugin::Initialize(TransportServer* ServerInterface) -{ - m_Impl->Start(m_BasePort, ServerInterface); -} - -void -SocketTransportPlugin::Shutdown() -{ - m_Impl->Stop(); -} - -bool -SocketTransportPlugin::IsAvailable() -{ - return true; -} - TransportPlugin* -CreateSocketTransportPlugin(uint16_t BasePort, unsigned int ThreadCount) +CreateSocketTransportPlugin() { - return new SocketTransportPlugin(BasePort, ThreadCount); + return new SocketTransportPluginImpl; } } // namespace zen diff --git a/src/zenhttp/transports/winsocktransport.h b/src/zenhttp/transports/winsocktransport.h index 2b2a55aef..59f25d5d3 100644 --- a/src/zenhttp/transports/winsocktransport.h +++ b/src/zenhttp/transports/winsocktransport.h @@ -8,7 +8,7 @@ namespace zen { -TransportPlugin* CreateSocketTransportPlugin(uint16_t BasePort, unsigned int ThreadCount); +TransportPlugin* CreateSocketTransportPlugin(); } // namespace zen |