aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-03-12 17:02:01 +0100
committerGitHub Enterprise <[email protected]>2026-03-12 17:02:01 +0100
commit3aa6aa83d05249d7081a8c19a28ce9b9c4566da2 (patch)
treef4f14006e82cdf0ed05083c9af90e17116614368 /src
parentUpdate CHANGELOG.md (diff)
downloadzen-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.cpp2
-rw-r--r--src/zenhttp/include/zenhttp/httpserver.h9
-rw-r--r--src/zenhttp/servers/httpasio.cpp35
-rw-r--r--src/zenhttp/servers/httpasio.h1
-rw-r--r--src/zenserver-test/zenserver-test.cpp31
-rw-r--r--src/zenserver/config/config.cpp27
-rw-r--r--src/zenserver/proxy/zenproxyserver.cpp4
-rw-r--r--src/zenserver/proxy/zenproxyserver.h15
-rw-r--r--src/zenserver/storage/zenstorageserver.cpp4
-rw-r--r--src/zenserver/zenserver.cpp1
-rw-r--r--src/zenserver/zenserver.h1
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";