diff options
| author | Dan Engelbrecht <[email protected]> | 2023-11-09 20:50:46 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-11-09 20:50:46 +0100 |
| commit | c289d408765fe47d54987bd49fcf277f2419104a (patch) | |
| tree | d76ac1fae0a2413f080c4ecfb0e7f281ed50f271 /src | |
| parent | 0.2.31-pre1 (diff) | |
| download | zen-c289d408765fe47d54987bd49fcf277f2419104a.tar.xz zen-c289d408765fe47d54987bd49fcf277f2419104a.zip | |
option for zenserver - `--http-forceloopback` (#516)
* New option for zenserver - `--http-forceloopback` which forces opening of the server http server using loopback (local) connection (UE-199776)
* add fallback to local connection for asio if we get access denied on public port
Diffstat (limited to 'src')
| -rw-r--r-- | src/zenhttp/httpserver.cpp | 5 | ||||
| -rw-r--r-- | src/zenhttp/include/zenhttp/httpserver.h | 3 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpasio.cpp | 47 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpasio.h | 2 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpsys.cpp | 27 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpsys.h | 1 | ||||
| -rw-r--r-- | src/zenserver/config.cpp | 12 |
7 files changed, 67 insertions, 30 deletions
diff --git a/src/zenhttp/httpserver.cpp b/src/zenhttp/httpserver.cpp index 9c303c62d..fa75060db 100644 --- a/src/zenhttp/httpserver.cpp +++ b/src/zenhttp/httpserver.cpp @@ -732,7 +732,7 @@ CreateHttpServerClass(HttpServerClass Class, const HttpServerConfig& Config) default: case HttpServerClass::kHttpAsio: ZEN_INFO("using asio HTTP server implementation"); - return CreateHttpAsioServer(Config.ThreadCount); + return CreateHttpAsioServer(Config.ForceLoopback, Config.ThreadCount); case HttpServerClass::kHttpMulti: { @@ -784,7 +784,8 @@ CreateHttpServerClass(HttpServerClass Class, const HttpServerConfig& Config) .AsyncWorkThreadCount = Config.HttpSys.AsyncWorkThreadCount, .IsAsyncResponseEnabled = Config.HttpSys.IsAsyncResponseEnabled, .IsRequestLoggingEnabled = Config.HttpSys.IsRequestLoggingEnabled, - .IsDedicatedServer = Config.IsDedicatedServer})); + .IsDedicatedServer = Config.IsDedicatedServer, + .ForceLoopback = Config.ForceLoopback})); #endif case HttpServerClass::kHttpNull: diff --git a/src/zenhttp/include/zenhttp/httpserver.h b/src/zenhttp/include/zenhttp/httpserver.h index 5d01e380a..eabad4728 100644 --- a/src/zenhttp/include/zenhttp/httpserver.h +++ b/src/zenhttp/include/zenhttp/httpserver.h @@ -186,7 +186,8 @@ struct HttpServerConfig { bool IsDedicatedServer = false; // Should be set to true for shared servers std::string ServerClass; // Choice of HTTP server implementation - unsigned int ThreadCount = 0; + bool ForceLoopback = false; + unsigned int ThreadCount = 0; struct { diff --git a/src/zenhttp/servers/httpasio.cpp b/src/zenhttp/servers/httpasio.cpp index 75852fe89..c62aca001 100644 --- a/src/zenhttp/servers/httpasio.cpp +++ b/src/zenhttp/servers/httpasio.cpp @@ -62,7 +62,7 @@ public: HttpAsioServerImpl(); ~HttpAsioServerImpl(); - int Start(uint16_t Port, int ThreadCount); + int Start(uint16_t Port, bool ForceLooopback, int ThreadCount); void Stop(); void RegisterService(const char* UrlPath, HttpService& Service); HttpService* RouteRequest(std::string_view Url); @@ -569,7 +569,7 @@ HttpServerConnection::HandleRequest() struct HttpAcceptor { - HttpAcceptor(HttpAsioServerImpl& Server, asio::io_service& IoService, uint16_t BasePort) + HttpAcceptor(HttpAsioServerImpl& Server, asio::io_service& IoService, uint16_t BasePort, bool ForceLoopback) : m_Server(Server) , m_IoService(IoService) , m_Acceptor(m_IoService, asio::ip::tcp::v6()) @@ -587,25 +587,36 @@ struct HttpAcceptor m_Acceptor.set_option(asio::socket_base::receive_buffer_size(128 * 1024)); m_Acceptor.set_option(asio::socket_base::send_buffer_size(256 * 1024)); - uint16_t EffectivePort = BasePort; + asio::ip::address_v6 BindAddress = ForceLoopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); + uint16_t EffectivePort = BasePort; asio::error_code BindErrorCode; - m_Acceptor.bind(asio::ip::tcp::endpoint(asio::ip::address_v6::any(), EffectivePort), BindErrorCode); + m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); + if (BindErrorCode == asio::error::access_denied && !BindAddress.is_loopback()) + { + // Access denied for a public port - lets try fall back to local port only + BindAddress = asio::ip::address_v6::loopback(); + m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); + } // Sharing violation implies the port is being used by another process for (uint16_t PortOffset = 1; (BindErrorCode == asio::error::address_in_use) && (PortOffset < 10); ++PortOffset) { EffectivePort = BasePort + (PortOffset * 100); - m_Acceptor.bind(asio::ip::tcp::endpoint(asio::ip::address_v6::any(), EffectivePort), BindErrorCode); + m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); } if (BindErrorCode == asio::error::access_denied) { EffectivePort = 0; - m_Acceptor.bind(asio::ip::tcp::endpoint(asio::ip::address_v6::any(), EffectivePort), BindErrorCode); + m_Acceptor.bind(asio::ip::tcp::endpoint(BindAddress, EffectivePort), BindErrorCode); } if (BindErrorCode) { ZEN_ERROR("Unable open asio service, error '{}'", BindErrorCode.message()); } + else if (BindAddress.is_loopback()) + { + ZEN_INFO("Registered local-only handler 'http://{}:{}/' - this is not accessible from remote hosts", "[::1]", EffectivePort); + } #if ZEN_PLATFORM_WINDOWS // On Windows, loopback connections can take advantage of a faster code path optionally with this flag. @@ -627,7 +638,7 @@ struct HttpAcceptor #endif m_Acceptor.listen(); - ZEN_INFO("Started asio server at port '{}'", EffectivePort); + ZEN_INFO("Started asio server at 'http://{}:{}'", BindAddress.is_loopback() ? "[::1]" : "*", EffectivePort); } void Start() @@ -831,13 +842,13 @@ HttpAsioServerImpl::~HttpAsioServerImpl() } int -HttpAsioServerImpl::Start(uint16_t Port, int ThreadCount) +HttpAsioServerImpl::Start(uint16_t Port, bool ForceLooopback, int ThreadCount) { ZEN_ASSERT(ThreadCount > 0); ZEN_INFO("starting asio http with {} service threads", ThreadCount); - m_Acceptor.reset(new asio_http::HttpAcceptor(*this, m_IoService, Port)); + m_Acceptor.reset(new asio_http::HttpAcceptor(*this, m_IoService, Port, ForceLooopback)); m_Acceptor->Start(); // This should consist of a set of minimum threads and grow on demand to @@ -926,7 +937,7 @@ namespace zen { class HttpAsioServer : public HttpServer { public: - HttpAsioServer(unsigned int ThreadCount); + HttpAsioServer(bool ForceLoopback, unsigned int ThreadCount); ~HttpAsioServer(); virtual void RegisterService(HttpService& Service) override; @@ -937,14 +948,16 @@ public: private: Event m_ShutdownEvent; - int m_BasePort = 0; - unsigned int m_ThreadCount = 0; + int m_BasePort = 0; + bool m_ForceLoopback = false; + unsigned int m_ThreadCount = 0; std::unique_ptr<asio_http::HttpAsioServerImpl> m_Impl; }; -HttpAsioServer::HttpAsioServer(unsigned int ThreadCount) -: m_ThreadCount(ThreadCount != 0 ? ThreadCount : Max(std::thread::hardware_concurrency(), 8u)) +HttpAsioServer::HttpAsioServer(bool ForceLoopback, unsigned int ThreadCount) +: m_ForceLoopback(ForceLoopback) +, m_ThreadCount(ThreadCount != 0 ? ThreadCount : Max(std::thread::hardware_concurrency(), 8u)) , m_Impl(std::make_unique<asio_http::HttpAsioServerImpl>()) { ZEN_DEBUG("Request object size: {} ({:#x})", sizeof(HttpRequestParser), sizeof(HttpRequestParser)); @@ -981,7 +994,7 @@ HttpAsioServer::RegisterService(HttpService& Service) int HttpAsioServer::Initialize(int BasePort) { - m_BasePort = m_Impl->Start(gsl::narrow<uint16_t>(BasePort), m_ThreadCount); + m_BasePort = m_Impl->Start(gsl::narrow<uint16_t>(BasePort), m_ForceLoopback, m_ThreadCount); return m_BasePort; } @@ -1036,9 +1049,9 @@ HttpAsioServer::RequestExit() } Ref<HttpServer> -CreateHttpAsioServer(unsigned int ThreadCount) +CreateHttpAsioServer(bool ForceLoopback, unsigned int ThreadCount) { - return Ref<HttpServer>{new HttpAsioServer(ThreadCount)}; + return Ref<HttpServer>{new HttpAsioServer(ForceLoopback, ThreadCount)}; } } // namespace zen diff --git a/src/zenhttp/servers/httpasio.h b/src/zenhttp/servers/httpasio.h index 2366f3437..36f988a65 100644 --- a/src/zenhttp/servers/httpasio.h +++ b/src/zenhttp/servers/httpasio.h @@ -6,6 +6,6 @@ namespace zen { -Ref<HttpServer> CreateHttpAsioServer(unsigned int ThreadCount); +Ref<HttpServer> CreateHttpAsioServer(bool ForceLoopback, unsigned int ThreadCount); } // namespace zen diff --git a/src/zenhttp/servers/httpsys.cpp b/src/zenhttp/servers/httpsys.cpp index 4eeab6662..0b11d396b 100644 --- a/src/zenhttp/servers/httpsys.cpp +++ b/src/zenhttp/servers/httpsys.cpp @@ -1003,18 +1003,27 @@ HttpSysServer::InitializeServer(int BasePort) int EffectivePort = BasePort; - Result = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, WildcardUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0); - - if ((Result == ERROR_SHARING_VIOLATION) && AllowPortProbing) + if (m_InitialConfig.ForceLoopback) + { + // Force trigger of opening using local port + ZEN_ASSERT(AllowLocalOnly); + Result = ERROR_ACCESS_DENIED; + } + else { - // Sharing violation implies the port is being used by another process - for (int PortOffset = 1; (Result == ERROR_SHARING_VIOLATION) && (PortOffset < 10); ++PortOffset) + Result = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, WildcardUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0); + + if ((Result == ERROR_SHARING_VIOLATION) && AllowPortProbing) { - EffectivePort = BasePort + (PortOffset * 100); - WildcardUrlPath.Reset(); - WildcardUrlPath << u8"http://*:"sv << int64_t(EffectivePort) << u8"/"sv; + // 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); + Result = HttpAddUrlToUrlGroup(m_HttpUrlGroupId, WildcardUrlPath.c_str(), HTTP_URL_CONTEXT(0), 0); + } } } diff --git a/src/zenhttp/servers/httpsys.h b/src/zenhttp/servers/httpsys.h index 6a6b16525..b2fe7475b 100644 --- a/src/zenhttp/servers/httpsys.h +++ b/src/zenhttp/servers/httpsys.h @@ -21,6 +21,7 @@ struct HttpSysConfig bool IsAsyncResponseEnabled = true; bool IsRequestLoggingEnabled = false; bool IsDedicatedServer = false; + bool ForceLoopback = false; }; Ref<HttpServer> CreateHttpSysServer(HttpSysConfig Config); diff --git a/src/zenserver/config.cpp b/src/zenserver/config.cpp index 8fe4213cb..08ba6dc95 100644 --- a/src/zenserver/config.cpp +++ b/src/zenserver/config.cpp @@ -99,6 +99,10 @@ ValidateOptions(ZenServerOptions& ServerOptions) throw zen::OptionParseException("Invalid AES initialization vector"); } } + if (ServerOptions.HttpServerConfig.ForceLoopback && ServerOptions.IsDedicated) + { + throw zen::OptionParseException("Dedicated server can not be used with forced local server address"); + } } UpstreamCachePolicy @@ -788,6 +792,7 @@ ParseConfigFile(const std::filesystem::path& Path, LuaOptions.AddOption("network.httpserverclass"sv, ServerOptions.HttpServerConfig.ServerClass, "http"sv); LuaOptions.AddOption("network.httpserverthreads"sv, ServerOptions.HttpServerConfig.ThreadCount, "http-threads"sv); LuaOptions.AddOption("network.port"sv, ServerOptions.BasePort, "port"sv); + LuaOptions.AddOption("network.forceloopback"sv, ServerOptions.HttpServerConfig.ForceLoopback, "http-forceloopback"sv); LuaOptions.AddOption("network.httpsys.async.workthreads"sv, ServerOptions.HttpServerConfig.HttpSys.AsyncWorkThreadCount, @@ -1031,6 +1036,13 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions) cxxopts::value<int>(ServerOptions.BasePort)->default_value("8558"), "<port number>"); + options.add_option("network", + "", + "http-forceloopback", + "Force using local loopback interface", + cxxopts::value<bool>(ServerOptions.HttpServerConfig.ForceLoopback)->default_value("false"), + "<http forceloopback>"); + options.add_option("httpsys", "", "httpsys-async-work-threads", |