diff options
| author | Stefan Boberg <[email protected]> | 2026-03-12 17:02:01 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-03-12 17:02:01 +0100 |
| commit | 3aa6aa83d05249d7081a8c19a28ce9b9c4566da2 (patch) | |
| tree | f4f14006e82cdf0ed05083c9af90e17116614368 /src | |
| parent | Update CHANGELOG.md (diff) | |
| download | zen-3aa6aa83d05249d7081a8c19a28ce9b9c4566da2.tar.xz zen-3aa6aa83d05249d7081a8c19a28ce9b9c4566da2.zip | |
Add --no-network option (#831)
- Add `--no-network` CLI option which disables all TCP/HTTPS listeners, restricting zenserver to Unix domain socket communication only.
- Also fixes asio upgrade breakage on main
Diffstat (limited to 'src')
| -rw-r--r-- | src/zenhttp/httpserver.cpp | 2 | ||||
| -rw-r--r-- | src/zenhttp/include/zenhttp/httpserver.h | 9 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpasio.cpp | 35 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpasio.h | 1 | ||||
| -rw-r--r-- | src/zenserver-test/zenserver-test.cpp | 31 | ||||
| -rw-r--r-- | src/zenserver/config/config.cpp | 27 | ||||
| -rw-r--r-- | src/zenserver/proxy/zenproxyserver.cpp | 4 | ||||
| -rw-r--r-- | src/zenserver/proxy/zenproxyserver.h | 15 | ||||
| -rw-r--r-- | src/zenserver/storage/zenstorageserver.cpp | 4 | ||||
| -rw-r--r-- | src/zenserver/zenserver.cpp | 1 | ||||
| -rw-r--r-- | src/zenserver/zenserver.h | 1 |
11 files changed, 101 insertions, 29 deletions
diff --git a/src/zenhttp/httpserver.cpp b/src/zenhttp/httpserver.cpp index 1a0018908..6ba0ca563 100644 --- a/src/zenhttp/httpserver.cpp +++ b/src/zenhttp/httpserver.cpp @@ -1157,7 +1157,7 @@ CreateHttpServerClass(const std::string_view ServerClass, const HttpServerConfig ZEN_INFO("using asio HTTP server implementation") return CreateHttpAsioServer(AsioConfig { .ThreadCount = Config.ThreadCount, .ForceLoopback = Config.ForceLoopback, .IsDedicatedServer = Config.IsDedicatedServer, - .UnixSocketPath = Config.UnixSocketPath, + .NoNetwork = Config.NoNetwork, .UnixSocketPath = Config.UnixSocketPath, #if ZEN_USE_OPENSSL .HttpsPort = Config.HttpsPort, .CertFile = Config.CertFile, .KeyFile = Config.KeyFile, #endif diff --git a/src/zenhttp/include/zenhttp/httpserver.h b/src/zenhttp/include/zenhttp/httpserver.h index d98877d16..627e7921f 100644 --- a/src/zenhttp/include/zenhttp/httpserver.h +++ b/src/zenhttp/include/zenhttp/httpserver.h @@ -329,10 +329,11 @@ struct HttpServerConfig std::vector<HttpServerPluginConfig> PluginConfigs; bool ForceLoopback = false; unsigned int ThreadCount = 0; - std::string UnixSocketPath; // Unix domain socket path (empty = disabled, non-Windows only) - int HttpsPort = 0; // HTTPS listen port (0 = disabled, ASIO backend) - std::string CertFile; // PEM certificate chain file path - std::string KeyFile; // PEM private key file path + std::string UnixSocketPath; // Unix domain socket path (empty = disabled, non-Windows only) + bool NoNetwork = false; // Disable TCP/HTTPS listeners; only accept connections via UnixSocketPath + int HttpsPort = 0; // HTTPS listen port (0 = disabled, ASIO backend) + std::string CertFile; // PEM certificate chain file path + std::string KeyFile; // PEM private key file path struct { diff --git a/src/zenhttp/servers/httpasio.cpp b/src/zenhttp/servers/httpasio.cpp index e8b27da70..643f33618 100644 --- a/src/zenhttp/servers/httpasio.cpp +++ b/src/zenhttp/servers/httpasio.cpp @@ -2157,15 +2157,18 @@ HttpAsioServerImpl::Start(uint16_t Port, const AsioConfig& Config) ZEN_INFO("starting asio http with {} service threads", Config.ThreadCount); - m_Acceptor.reset( - new asio_http::HttpAcceptor(*this, m_IoService, Port, Config.ForceLoopback, /*AllowPortProbing */ !Config.IsDedicatedServer)); - - if (!m_Acceptor->IsValid()) + if (!Config.NoNetwork) { - return 0; - } + m_Acceptor.reset( + new asio_http::HttpAcceptor(*this, m_IoService, Port, Config.ForceLoopback, /*AllowPortProbing */ !Config.IsDedicatedServer)); + + if (!m_Acceptor->IsValid()) + { + return 0; + } - m_Acceptor->Start(); + m_Acceptor->Start(); + } #if defined(ASIO_HAS_LOCAL_SOCKETS) if (!Config.UnixSocketPath.empty()) @@ -2184,7 +2187,7 @@ HttpAsioServerImpl::Start(uint16_t Port, const AsioConfig& Config) #endif #if ZEN_USE_OPENSSL - if (!Config.CertFile.empty() && !Config.KeyFile.empty()) + if (!Config.NoNetwork && !Config.CertFile.empty() && !Config.KeyFile.empty()) { m_SslContext = std::make_unique<asio::ssl::context>(asio::ssl::context::tlsv12_server); m_SslContext->set_options(asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3 | @@ -2243,12 +2246,18 @@ HttpAsioServerImpl::Start(uint16_t Port, const AsioConfig& Config) }); } - ZEN_INFO("asio http started in {} mode, using {} threads on port {}", - Config.IsDedicatedServer ? "DEDICATED" : "NORMAL", - Config.ThreadCount, - m_Acceptor->GetAcceptPort()); + if (m_Acceptor) + { + ZEN_INFO("asio http started in {} mode, using {} threads on port {}", + Config.IsDedicatedServer ? "DEDICATED" : "NORMAL", + Config.ThreadCount, + m_Acceptor->GetAcceptPort()); + + return m_Acceptor->GetAcceptPort(); + } - return m_Acceptor->GetAcceptPort(); + ZEN_INFO("asio http started in no-network mode, using {} threads (unix socket only)", Config.ThreadCount); + return Port; } void diff --git a/src/zenhttp/servers/httpasio.h b/src/zenhttp/servers/httpasio.h index 5adf4d5e8..21d10170e 100644 --- a/src/zenhttp/servers/httpasio.h +++ b/src/zenhttp/servers/httpasio.h @@ -11,6 +11,7 @@ struct AsioConfig unsigned int ThreadCount = 0; bool ForceLoopback = false; bool IsDedicatedServer = false; + bool NoNetwork = false; std::string UnixSocketPath; #if ZEN_USE_OPENSSL int HttpsPort = 0; // 0 = auto-assign; set CertFile/KeyFile to enable HTTPS diff --git a/src/zenserver-test/zenserver-test.cpp b/src/zenserver-test/zenserver-test.cpp index 0b2bc50c0..e89812c1f 100644 --- a/src/zenserver-test/zenserver-test.cpp +++ b/src/zenserver-test/zenserver-test.cpp @@ -380,6 +380,37 @@ TEST_CASE("http.unixsocket") CHECK(Res.ResponsePayload.GetView().EqualBytes(Body.GetView())); } } + +TEST_CASE("http.nonetwork") +{ + std::filesystem::path TestDir = TestEnv.CreateNewTestDir(); + std::filesystem::path SocketDir = TestEnv.CreateNewTestDir(); + std::string SocketPath = (SocketDir / "zen.sock").string(); + + ZenServerInstance Instance(TestEnv); + Instance.SetDataDir(TestDir); + const uint16_t PortNumber = Instance.SpawnServerAndWaitUntilReady(fmt::format("--http=asio --no-network --unix-socket {}", SocketPath)); + + // Verify communication works via Unix socket + HttpClientSettings Settings; + Settings.UnixSocketPath = SocketPath; + HttpClient Http{fmt::format("http://localhost:{}", PortNumber), Settings, {}}; + + SUBCASE("GET over unix socket succeeds") + { + HttpClient::Response Res = Http.Get("/testing/hello"); + CHECK(Res.IsSuccess()); + } + + SUBCASE("TCP connection is refused") + { + asio::io_context IoContext; + asio::ip::tcp::socket Socket(IoContext); + asio::error_code Ec; + Socket.connect(asio::ip::tcp::endpoint(asio::ip::make_address("127.0.0.1"), PortNumber), Ec); + CHECK(Ec); // Expect an error (connection refused) + } +} # endif TEST_SUITE_END(); diff --git a/src/zenserver/config/config.cpp b/src/zenserver/config/config.cpp index 858225032..c550b174c 100644 --- a/src/zenserver/config/config.cpp +++ b/src/zenserver/config/config.cpp @@ -153,6 +153,7 @@ ZenServerConfiguratorBase::AddCommonConfigOptions(LuaConfig::Options& LuaOptions LuaOptions.AddOption("network.port"sv, ServerOptions.BasePort, "port"sv); LuaOptions.AddOption("network.forceloopback"sv, ServerOptions.HttpConfig.ForceLoopback, "http-forceloopback"sv); LuaOptions.AddOption("network.unixsocket"sv, ServerOptions.HttpConfig.UnixSocketPath, "unix-socket"sv); + LuaOptions.AddOption("network.nonetwork"sv, ServerOptions.HttpConfig.NoNetwork, "no-network"sv); LuaOptions.AddOption("network.https.port"sv, ServerOptions.HttpConfig.HttpsPort, "https-port"sv); LuaOptions.AddOption("network.https.certfile"sv, ServerOptions.HttpConfig.CertFile, "cert-file"sv); LuaOptions.AddOption("network.https.keyfile"sv, ServerOptions.HttpConfig.KeyFile, "key-file"sv); @@ -324,6 +325,13 @@ ZenServerCmdLineOptions::AddCliOptions(cxxopts::Options& options, ZenServerConfi options.add_option("network", "", + "no-network", + "Disable TCP/HTTPS listeners; only accept connections via --unix-socket", + cxxopts::value<bool>(ServerOptions.HttpConfig.NoNetwork)->default_value("false"), + ""); + + options.add_option("network", + "", "https-port", "HTTPS listen port (0 = disabled)", cxxopts::value<int>(ServerOptions.HttpConfig.HttpsPort)->default_value("0"), @@ -513,6 +521,25 @@ ZenServerCmdLineOptions::ApplyOptions(cxxopts::Options& options, ZenServerConfig } #endif + // Validate --no-network + if (ServerOptions.HttpConfig.NoNetwork) + { + if (ServerOptions.HttpConfig.UnixSocketPath.empty()) + { + throw OptionParseException("'--no-network' requires '--unix-socket' to be set", options.help()); + } +#if ZEN_WITH_HTTPSYS + if (ServerOptions.HttpConfig.ServerClass == "httpsys") + { + throw OptionParseException("'--no-network' is not compatible with '--http=httpsys'", options.help()); + } +#endif + if (ServerOptions.HttpConfig.ServerClass.empty()) + { + ServerOptions.HttpConfig.ServerClass = "asio"; + } + } + // Validate generic HTTPS options (used by ASIO backend) if (ServerOptions.HttpConfig.HttpsPort > 0) { diff --git a/src/zenserver/proxy/zenproxyserver.cpp b/src/zenserver/proxy/zenproxyserver.cpp index 1fd9cd2c4..acfdad45f 100644 --- a/src/zenserver/proxy/zenproxyserver.cpp +++ b/src/zenserver/proxy/zenproxyserver.cpp @@ -302,7 +302,7 @@ ZenProxyServer::Initialize(const ZenProxyServerConfig& ServerConfig, ZenServerSt // Keep the io_context alive even when there is no pending work, so that // worker threads don't exit prematurely between async operations. - m_ProxyIoWorkGuard = std::make_unique<asio::io_context::work>(m_ProxyIoContext); + m_ProxyIoWorkGuard.emplace(m_ProxyIoContext.get_executor()); // Start proxy I/O worker threads. Use a modest thread count — proxy work is // I/O-bound so we don't need a thread per core, but having more than one @@ -404,7 +404,7 @@ ZenProxyServer::Cleanup() Service->Stop(); } - m_ProxyIoWorkGuard.reset(); + m_ProxyIoWorkGuard.reset(); // releases the work guard, allowing io_context to finish m_ProxyIoContext.stop(); for (auto& Thread : m_ProxyIoThreads) { diff --git a/src/zenserver/proxy/zenproxyserver.h b/src/zenserver/proxy/zenproxyserver.h index 7dad748cf..329ca5235 100644 --- a/src/zenserver/proxy/zenproxyserver.h +++ b/src/zenserver/proxy/zenproxyserver.h @@ -7,6 +7,7 @@ #include "proxy/tcpproxy.h" #include <memory> +#include <optional> #include <thread> #include <vector> @@ -84,13 +85,13 @@ public: void Cleanup(); private: - asio::io_context m_ProxyIoContext; - std::unique_ptr<asio::io_context::work> m_ProxyIoWorkGuard; - std::vector<std::thread> m_ProxyIoThreads; - std::vector<std::unique_ptr<TcpProxyService>> m_ProxyServices; - std::unique_ptr<HttpApiService> m_ApiService; - std::unique_ptr<HttpFrontendService> m_FrontendService; - std::unique_ptr<HttpProxyStatsService> m_ProxyStatsService; + asio::io_context m_ProxyIoContext; + std::optional<asio::executor_work_guard<asio::io_context::executor_type>> m_ProxyIoWorkGuard; + std::vector<std::thread> m_ProxyIoThreads; + std::vector<std::unique_ptr<TcpProxyService>> m_ProxyServices; + std::unique_ptr<HttpApiService> m_ApiService; + std::unique_ptr<HttpFrontendService> m_FrontendService; + std::unique_ptr<HttpProxyStatsService> m_ProxyStatsService; }; } // namespace zen diff --git a/src/zenserver/storage/zenstorageserver.cpp b/src/zenserver/storage/zenstorageserver.cpp index d4b8e37ef..77588bd6c 100644 --- a/src/zenserver/storage/zenstorageserver.cpp +++ b/src/zenserver/storage/zenstorageserver.cpp @@ -725,11 +725,11 @@ ZenStorageServer::Run() ZEN_INFO(ZEN_APP_NAME " now running (pid: {})", GetCurrentProcessId()); - if (m_FrontendService) + if (m_FrontendService && !m_NoNetwork) { ZEN_INFO("frontend link: {}", m_Http->GetServiceUri(m_FrontendService.get())); } - else + else if (!m_FrontendService) { ZEN_INFO("frontend service disabled"); } diff --git a/src/zenserver/zenserver.cpp b/src/zenserver/zenserver.cpp index ad8d18dc0..8283f0cbe 100644 --- a/src/zenserver/zenserver.cpp +++ b/src/zenserver/zenserver.cpp @@ -101,6 +101,7 @@ ZenServerBase::Initialize(const ZenServerConfig& ServerOptions, ZenServerState:: ZEN_MEMSCOPE(GetZenserverTag()); m_IsPowerCycle = ServerOptions.IsPowerCycle; + m_NoNetwork = ServerOptions.HttpConfig.NoNetwork; const std::string MutexName = fmt::format("zen_{}", ServerOptions.BasePort); diff --git a/src/zenserver/zenserver.h b/src/zenserver/zenserver.h index 2b9d68aee..374184aa9 100644 --- a/src/zenserver/zenserver.h +++ b/src/zenserver/zenserver.h @@ -67,6 +67,7 @@ protected: bool m_IsDedicatedMode = false; bool m_TestMode = false; + bool m_NoNetwork = false; bool m_DebugOptionForcedCrash = false; std::string m_ServerMode = "Server"; |