aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzousar <[email protected]>2022-01-27 01:01:05 -0700
committerGitHub <[email protected]>2022-01-27 09:01:05 +0100
commitcf38d96543b2086a863ef4823ee769ec45cc45a4 (patch)
tree1c5c56053f43271398d9f9a469383f29df76c570
parentImplement SkipData,QueryLocal,StoreLocal for HandleRpcGetCacheRecords (#41) (diff)
downloadzen-cf38d96543b2086a863ef4823ee769ec45cc45a4.tar.xz
zen-cf38d96543b2086a863ef4823ee769ec45cc45a4.zip
Handle HTTP port collisions when initializing server (#40)
-rw-r--r--zenhttp/httpasio.cpp22
-rw-r--r--zenhttp/httpasio.h2
-rw-r--r--zenhttp/httpnull.cpp4
-rw-r--r--zenhttp/httpnull.h2
-rw-r--r--zenhttp/httpsys.cpp55
-rw-r--r--zenhttp/httpsys.h4
-rw-r--r--zenhttp/include/zenhttp/httpserver.h2
-rw-r--r--zenserver/zenserver.cpp31
-rw-r--r--zenutil/include/zenutil/zenserverprocess.h9
-rw-r--r--zenutil/zenserverprocess.cpp28
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