diff options
| author | zousar <[email protected]> | 2022-01-27 01:01:05 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-01-27 09:01:05 +0100 |
| commit | cf38d96543b2086a863ef4823ee769ec45cc45a4 (patch) | |
| tree | 1c5c56053f43271398d9f9a469383f29df76c570 | |
| parent | Implement SkipData,QueryLocal,StoreLocal for HandleRpcGetCacheRecords (#41) (diff) | |
| download | zen-cf38d96543b2086a863ef4823ee769ec45cc45a4.tar.xz zen-cf38d96543b2086a863ef4823ee769ec45cc45a4.zip | |
Handle HTTP port collisions when initializing server (#40)
| -rw-r--r-- | zenhttp/httpasio.cpp | 22 | ||||
| -rw-r--r-- | zenhttp/httpasio.h | 2 | ||||
| -rw-r--r-- | zenhttp/httpnull.cpp | 4 | ||||
| -rw-r--r-- | zenhttp/httpnull.h | 2 | ||||
| -rw-r--r-- | zenhttp/httpsys.cpp | 55 | ||||
| -rw-r--r-- | zenhttp/httpsys.h | 4 | ||||
| -rw-r--r-- | zenhttp/include/zenhttp/httpserver.h | 2 | ||||
| -rw-r--r-- | zenserver/zenserver.cpp | 31 | ||||
| -rw-r--r-- | zenutil/include/zenutil/zenserverprocess.h | 9 | ||||
| -rw-r--r-- | zenutil/zenserverprocess.cpp | 28 |
10 files changed, 103 insertions, 56 deletions
diff --git a/zenhttp/httpasio.cpp b/zenhttp/httpasio.cpp index 801bb51ac..f2d48200e 100644 --- a/zenhttp/httpasio.cpp +++ b/zenhttp/httpasio.cpp @@ -64,7 +64,7 @@ public: HttpAsioServerImpl(); ~HttpAsioServerImpl(); - void Start(uint16_t Port, int ThreadCount); + int Start(uint16_t Port, int ThreadCount); void Stop(); void RegisterService(const char* UrlPath, HttpService& Service); HttpService* RouteRequest(std::string_view Url); @@ -934,7 +934,12 @@ struct HttpAcceptor m_Acceptor.set_option(asio::ip::v6_only(false)); m_Acceptor.set_option(asio::socket_base::reuse_address(true)); m_Acceptor.set_option(asio::ip::tcp::no_delay(true)); - m_Acceptor.bind(asio::ip::tcp::endpoint(asio::ip::address_v6::any(), Port)); + asio::error_code BindErrorCode; + m_Acceptor.bind(asio::ip::tcp::endpoint(asio::ip::address_v6::any(), Port), BindErrorCode); + if (BindErrorCode == asio::error::access_denied) + { + m_Acceptor.bind(asio::ip::tcp::endpoint(asio::ip::address_v6::any(), 0)); + } m_Acceptor.listen(); } @@ -980,6 +985,8 @@ struct HttpAcceptor }); } + int GetAcceptPort() { return m_Acceptor.local_endpoint().port(); } + private: HttpAsioServerImpl& m_Server; asio::io_service& m_IoService; @@ -1119,7 +1126,7 @@ HttpAsioServerImpl::~HttpAsioServerImpl() { } -void +int HttpAsioServerImpl::Start(uint16_t Port, int ThreadCount) { ZEN_ASSERT(ThreadCount > 0); @@ -1142,6 +1149,8 @@ HttpAsioServerImpl::Start(uint16_t Port, int ThreadCount) } }); } + + return m_Acceptor->GetAcceptPort(); } void @@ -1212,12 +1221,11 @@ HttpAsioServer::RegisterService(HttpService& Service) m_Impl->RegisterService(Service.BaseUri(), Service); } -void +int HttpAsioServer::Initialize(int BasePort) { - m_BasePort = BasePort; - - m_Impl->Start(gsl::narrow<uint16_t>(m_BasePort), Max(std::thread::hardware_concurrency(), 8u)); + m_BasePort = m_Impl->Start(gsl::narrow<uint16_t>(BasePort), Max(std::thread::hardware_concurrency(), 8u)); + return m_BasePort; } void diff --git a/zenhttp/httpasio.h b/zenhttp/httpasio.h index 08834ba21..716145955 100644 --- a/zenhttp/httpasio.h +++ b/zenhttp/httpasio.h @@ -22,7 +22,7 @@ public: ~HttpAsioServer(); virtual void RegisterService(HttpService& Service) override; - virtual void Initialize(int BasePort) override; + virtual int Initialize(int BasePort) override; virtual void Run(bool IsInteractiveSession) override; virtual void RequestExit() override; diff --git a/zenhttp/httpnull.cpp b/zenhttp/httpnull.cpp index 31b13a6ce..a6e1d3567 100644 --- a/zenhttp/httpnull.cpp +++ b/zenhttp/httpnull.cpp @@ -24,10 +24,10 @@ HttpNullServer::RegisterService(HttpService& Service) ZEN_UNUSED(Service); } -void +int HttpNullServer::Initialize(int BasePort) { - ZEN_UNUSED(BasePort); + return BasePort; } void diff --git a/zenhttp/httpnull.h b/zenhttp/httpnull.h index 867bbe4d2..74f021f6b 100644 --- a/zenhttp/httpnull.h +++ b/zenhttp/httpnull.h @@ -18,7 +18,7 @@ public: ~HttpNullServer(); virtual void RegisterService(HttpService& Service) override; - virtual void Initialize(int BasePort) override; + virtual int Initialize(int BasePort) override; virtual void Run(bool IsInteractiveSession) override; virtual void RequestExit() override; diff --git a/zenhttp/httpsys.cpp b/zenhttp/httpsys.cpp index b3d109b6a..3c57f7ce3 100644 --- a/zenhttp/httpsys.cpp +++ b/zenhttp/httpsys.cpp @@ -746,7 +746,7 @@ HttpSysServer::~HttpSysServer() } } -void +int HttpSysServer::InitializeServer(int BasePort) { using namespace std::literals; @@ -762,7 +762,7 @@ HttpSysServer::InitializeServer(int BasePort) { ZEN_ERROR("Failed to create server session for '{}': {:#x}", WideToUtf8(WildcardUrlPath), Result); - return; + return BasePort; } Result = HttpCreateUrlGroup(m_HttpSessionId, &m_HttpUrlGroupId, 0); @@ -771,17 +771,29 @@ HttpSysServer::InitializeServer(int BasePort) { ZEN_ERROR("Failed to create URL group for '{}': {:#x}", WideToUtf8(WildcardUrlPath), Result); - return; + return BasePort; } + int EffectivePort = BasePort; + Result = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, WildcardUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0); + // Sharing violation implies the port is being used by another process + for (int PortOffset = 1; (Result == ERROR_SHARING_VIOLATION) && (PortOffset < 10); ++PortOffset) + { + EffectivePort = BasePort + (PortOffset * 100); + WildcardUrlPath.Reset(); + WildcardUrlPath << u8"http://*:"sv << int64_t(EffectivePort) << u8"/"sv; + + Result = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, WildcardUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0); + } + m_BaseUris.clear(); if (Result == NO_ERROR) { m_BaseUris.push_back(WildcardUrlPath.c_str()); } - else + else if (Result == ERROR_ACCESS_DENIED) { // If we can't register the wildcard path, we fall back to local paths // This local paths allow requests originating locally to function, but will not allow @@ -792,14 +804,26 @@ HttpSysServer::InitializeServer(int BasePort) const std::u8string_view Hosts[] = {u8"[::1]"sv, u8"localhost"sv, u8"127.0.0.1"sv}; - for (const std::u8string_view Host : Hosts) + ULONG InternalResult = ERROR_SHARING_VIOLATION; + for (int PortOffset = 0; (InternalResult == ERROR_SHARING_VIOLATION) && (PortOffset < 10); ++PortOffset) { - WideStringBuilder<64> LocalUrlPath; - LocalUrlPath << u8"http://"sv << Host << u8":"sv << int64_t(BasePort) << u8"/"sv; + EffectivePort = BasePort + (PortOffset * 100); - if (HttpAddUrlToUrlGroup(m_HttpUrlGroupId, LocalUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0) == NO_ERROR) + for (const std::u8string_view Host : Hosts) { - m_BaseUris.push_back(LocalUrlPath.c_str()); + WideStringBuilder<64> LocalUrlPath; + LocalUrlPath << u8"http://"sv << Host << u8":"sv << int64_t(EffectivePort) << u8"/"sv; + + InternalResult = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, LocalUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0); + + if (InternalResult == NO_ERROR) + { + m_BaseUris.push_back(LocalUrlPath.c_str()); + } + else + { + break; + } } } } @@ -808,7 +832,7 @@ HttpSysServer::InitializeServer(int BasePort) { ZEN_ERROR("Failed to add base URL to URL group for '{}': {:#x}", WideToUtf8(WildcardUrlPath), Result); - return; + return BasePort; } HTTP_BINDING_INFO HttpBindingInfo = {{0}, 0}; @@ -823,7 +847,7 @@ HttpSysServer::InitializeServer(int BasePort) { ZEN_ERROR("Failed to create request queue for '{}': {:#x}", WideToUtf8(m_BaseUris.front()), Result); - return; + return EffectivePort; } HttpBindingInfo.Flags.Present = 1; @@ -835,7 +859,7 @@ HttpSysServer::InitializeServer(int BasePort) { ZEN_ERROR("Failed to set server binding property for '{}': {:#x}", WideToUtf8(m_BaseUris.front()), Result); - return; + return EffectivePort; } // Create I/O completion port @@ -853,6 +877,8 @@ HttpSysServer::InitializeServer(int BasePort) ZEN_INFO("Started http.sys server at '{}'", WideToUtf8(m_BaseUris.front())); } + + return EffectivePort; } void @@ -1603,11 +1629,12 @@ InitialRequestHandler::HandleCompletion(ULONG IoResult, ULONG_PTR NumberOfBytesT // HttpServer interface implementation // -void +int HttpSysServer::Initialize(int BasePort) { - InitializeServer(BasePort); + int EffectivePort = InitializeServer(BasePort); StartServer(); + return EffectivePort; } void diff --git a/zenhttp/httpsys.h b/zenhttp/httpsys.h index 06bad99c3..0453b8740 100644 --- a/zenhttp/httpsys.h +++ b/zenhttp/httpsys.h @@ -41,7 +41,7 @@ public: // HttpServer interface implementation - virtual void Initialize(int BasePort) override; + virtual int Initialize(int BasePort) override; virtual void Run(bool TestMode) override; virtual void RequestExit() override; virtual void RegisterService(HttpService& Service) override; @@ -52,7 +52,7 @@ public: inline bool IsAsyncResponseEnabled() const { return m_IsAsyncResponseEnabled; } private: - void InitializeServer(int BasePort); + int InitializeServer(int BasePort); void Cleanup(); void StartServer(); diff --git a/zenhttp/include/zenhttp/httpserver.h b/zenhttp/include/zenhttp/httpserver.h index 902310f04..545b96db2 100644 --- a/zenhttp/include/zenhttp/httpserver.h +++ b/zenhttp/include/zenhttp/httpserver.h @@ -169,7 +169,7 @@ class HttpServer : public RefCounted { public: virtual void RegisterService(HttpService& Service) = 0; - virtual void Initialize(int BasePort) = 0; + virtual int Initialize(int BasePort) = 0; virtual void Run(bool IsInteractiveSession) = 0; virtual void RequestExit() = 0; }; diff --git a/zenserver/zenserver.cpp b/zenserver/zenserver.cpp index 6a42a4044..c6a27ec44 100644 --- a/zenserver/zenserver.cpp +++ b/zenserver/zenserver.cpp @@ -155,7 +155,7 @@ namespace utils { class ZenServer : public IHttpStatusProvider { public: - void Initialize(const ZenServerOptions& ServerOptions, ZenServerState::ZenServerEntry* ServerEntry) + int Initialize(const ZenServerOptions& ServerOptions, ZenServerState::ZenServerEntry* ServerEntry) { m_UseSentry = ServerOptions.NoSentry == false; m_ServerEntry = ServerEntry; @@ -200,8 +200,8 @@ public: // Ok so now we're configured, let's kick things off - m_Http = zen::CreateHttpServer(ServerOptions.HttpServerClass); - m_Http->Initialize(ServerOptions.BasePort); + m_Http = zen::CreateHttpServer(ServerOptions.HttpServerClass); + int EffectiveBasePort = m_Http->Initialize(ServerOptions.BasePort); m_AuthService = std::make_unique<zen::HttpAuthService>(); m_Http->RegisterService(*m_AuthService); @@ -262,7 +262,7 @@ public: #if ZEN_ENABLE_MESH if (ServerOptions.MeshEnabled) { - StartMesh(BasePort); + StartMesh(EffectiveBasePort); } else { @@ -309,6 +309,8 @@ public: .Enabled = ServerOptions.GcConfig.Enabled, }; m_GcScheduler.Initialize(GcConfig); + + return EffectiveBasePort; } void InitializeState(const ZenServerOptions& ServerOptions); @@ -889,6 +891,20 @@ ZenEntryPoint::Run() Entry->AddSponsorProcess(ServerOptions.OwnerPid); } + ZenServer Server; + Server.SetDataRoot(ServerOptions.DataDir); + Server.SetContentRoot(ServerOptions.ContentDir); + Server.SetTestMode(ServerOptions.IsTest); + Server.SetDedicatedMode(ServerOptions.IsDedicated); + int EffectiveBasePort = Server.Initialize(ServerOptions, Entry); + + Entry->EffectiveListenPort = uint16_t(EffectiveBasePort); + if (EffectiveBasePort != ServerOptions.BasePort) + { + ZEN_INFO(ZEN_APP_NAME " - relocated to base port {}", EffectiveBasePort); + ServerOptions.BasePort = EffectiveBasePort; + } + std::unique_ptr<std::thread> ShutdownThread; std::unique_ptr<zen::NamedEvent> ShutdownEvent; @@ -896,13 +912,6 @@ ZenEntryPoint::Run() ShutdownEventName << "Zen_" << ServerOptions.BasePort << "_Shutdown"; ShutdownEvent.reset(new zen::NamedEvent{ShutdownEventName}); - ZenServer Server; - Server.SetDataRoot(ServerOptions.DataDir); - Server.SetContentRoot(ServerOptions.ContentDir); - Server.SetTestMode(ServerOptions.IsTest); - Server.SetDedicatedMode(ServerOptions.IsDedicated); - Server.Initialize(ServerOptions, Entry); - // Monitor shutdown signals ShutdownThread.reset(new std::thread{[&] { diff --git a/zenutil/include/zenutil/zenserverprocess.h b/zenutil/include/zenutil/zenserverprocess.h index 55b9a50cd..2a3146e2d 100644 --- a/zenutil/include/zenutil/zenserverprocess.h +++ b/zenutil/include/zenutil/zenserverprocess.h @@ -100,11 +100,12 @@ public: // additional state. For example, you can use the session ID // to introduce additional named objects std::atomic<uint32_t> Pid; - std::atomic<uint16_t> ListenPort; + std::atomic<uint16_t> DesiredListenPort; std::atomic<uint16_t> Flags; uint8_t SessionId[12]; std::atomic<uint32_t> SponsorPids[8]; - uint8_t Padding[12]; + std::atomic<uint16_t> EffectiveListenPort; + uint8_t Padding[10]; enum class FlagsEnum : uint16_t { @@ -125,8 +126,8 @@ public: void Initialize(); [[nodiscard]] bool InitializeReadOnly(); - [[nodiscard]] ZenServerEntry* Lookup(int ListenPort); - ZenServerEntry* Register(int ListenPort); + [[nodiscard]] ZenServerEntry* Lookup(int DesiredListenPort); + ZenServerEntry* Register(int DesiredListenPort); void Sweep(); void Snapshot(std::function<void(const ZenServerEntry&)>&& Callback); inline bool IsReadOnly() const { return m_IsReadOnly; } diff --git a/zenutil/zenserverprocess.cpp b/zenutil/zenserverprocess.cpp index fe6236d18..5bddc72bc 100644 --- a/zenutil/zenserverprocess.cpp +++ b/zenutil/zenserverprocess.cpp @@ -230,11 +230,11 @@ ZenServerState::InitializeReadOnly() } ZenServerState::ZenServerEntry* -ZenServerState::Lookup(int ListenPort) +ZenServerState::Lookup(int DesiredListenPort) { for (int i = 0; i < m_MaxEntryCount; ++i) { - if (m_Data[i].ListenPort == ListenPort) + if (m_Data[i].DesiredListenPort == DesiredListenPort) { return &m_Data[i]; } @@ -244,7 +244,7 @@ ZenServerState::Lookup(int ListenPort) } ZenServerState::ZenServerEntry* -ZenServerState::Register(int ListenPort) +ZenServerState::Register(int DesiredListenPort) { if (m_Data == nullptr) { @@ -259,17 +259,18 @@ ZenServerState::Register(int ListenPort) { ZenServerEntry& Entry = m_Data[i]; - if (Entry.ListenPort.load(std::memory_order_relaxed) == 0) + if (Entry.DesiredListenPort.load(std::memory_order_relaxed) == 0) { uint16_t Expected = 0; - if (Entry.ListenPort.compare_exchange_strong(Expected, uint16_t(ListenPort))) + if (Entry.DesiredListenPort.compare_exchange_strong(Expected, uint16_t(DesiredListenPort))) { // Successfully allocated entry m_OurEntry = &Entry; - Entry.Pid = Pid; - Entry.Flags = 0; + Entry.Pid = Pid; + Entry.EffectiveListenPort = 0; + Entry.Flags = 0; const Oid SesId = GetSessionId(); memcpy(Entry.SessionId, &SesId, sizeof SesId); @@ -296,11 +297,11 @@ ZenServerState::Sweep() { ZenServerEntry& Entry = m_Data[i]; - if (Entry.ListenPort) + if (Entry.DesiredListenPort) { if (IsProcessRunning(Entry.Pid) == false) { - ZEN_DEBUG("Sweep - pid {} not running, reclaiming entry (port {})", Entry.Pid, Entry.ListenPort); + ZEN_DEBUG("Sweep - pid {} not running, reclaiming entry (port {})", Entry.Pid, Entry.DesiredListenPort); Entry.Reset(); } @@ -320,7 +321,7 @@ ZenServerState::Snapshot(std::function<void(const ZenServerEntry&)>&& Callback) { ZenServerEntry& Entry = m_Data[i]; - if (Entry.ListenPort) + if (Entry.DesiredListenPort) { Callback(Entry); } @@ -330,9 +331,10 @@ ZenServerState::Snapshot(std::function<void(const ZenServerEntry&)>&& Callback) void ZenServerState::ZenServerEntry::Reset() { - Pid = 0; - ListenPort = 0; - Flags = 0; + Pid = 0; + DesiredListenPort = 0; + Flags = 0; + EffectiveListenPort = 0; } void |