diff options
| author | Stefan Boberg <[email protected]> | 2026-03-10 17:27:26 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-03-10 17:27:26 +0100 |
| commit | d0a07e555577dcd4a8f55f1b45d9e8e4e6366ab7 (patch) | |
| tree | 2dfe1e3e0b620043d358e0b7f8bdf8320d985491 /src/zen/zen.cpp | |
| parent | changelog entry which was inadvertently omitted from PR merge (diff) | |
| download | archived-zen-d0a07e555577dcd4a8f55f1b45d9e8e4e6366ab7.tar.xz archived-zen-d0a07e555577dcd4a8f55f1b45d9e8e4e6366ab7.zip | |
HttpClient using libcurl, Unix Sockets for HTTP. HTTPS support (#770)
The main goal of this change is to eliminate the cpr back-end altogether and replace it with the curl implementation. I would expect to drop cpr as soon as we feel happy with the libcurl back-end. That would leave us with a direct dependency on libcurl only, and cpr can be eliminated as a dependency.
### HttpClient Backend Overhaul
- Implemented a new **libcurl-based HttpClient** backend (`httpclientcurl.cpp`, ~2000 lines)
as an alternative to the cpr-based one
- Made HttpClient backend **configurable at runtime** via constructor arguments
and `-httpclient=...` CLI option (for zen, zenserver, and tests)
- Extended HttpClient test suite to cover multipart/content-range scenarios
### Unix Domain Socket Support
- Added Unix domain socket support to **httpasio** (server side)
- Added Unix domain socket support to **HttpClient**
- Added Unix domain socket support to **HttpWsClient** (WebSocket client)
- Templatized `HttpServerConnectionT<SocketType>` and `WsAsioConnectionT<SocketType>`
to handle TCP, Unix, and SSL sockets uniformly via `if constexpr` dispatch
### HTTPS Support
- Added **preliminary HTTPS support to httpasio** (for Mac/Linux via OpenSSL)
- Added **basic HTTPS support for http.sys** (Windows)
- Implemented HTTPS test for httpasio
- Split `InitializeServer` into smaller sub-functions for http.sys
### Other Notable Changes
- Improved **zenhttp-test stability** with dynamic port allocation
- Enhanced port retry logic in http.sys (handles ERROR_ACCESS_DENIED)
- Fatal signal/exception handlers for backtrace generation in tests
- Added `zen bench http` subcommand to exercise network + HTTP client/server communication stack
Diffstat (limited to 'src/zen/zen.cpp')
| -rw-r--r-- | src/zen/zen.cpp | 75 |
1 files changed, 71 insertions, 4 deletions
diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp index 9a466da2e..86c29344e 100644 --- a/src/zen/zen.cpp +++ b/src/zen/zen.cpp @@ -196,6 +196,7 @@ ZenCmdBase::GetSubCommand(cxxopts::Options&, ZenSubCmdBase::ZenSubCmdBase(std::string_view Name, std::string_view Description) : m_SubOptions(std::string(Name), std::string(Description)) +, m_Description(Description) { m_SubOptions.add_options()("h,help", "Print help"); } @@ -213,6 +214,35 @@ ZenCmdWithSubCommands::OnParentOptionsParsed(const ZenCliOptions& /*GlobalOption } void +ZenCmdWithSubCommands::PrintHelp() +{ + // Show all option groups except the internal "__hidden__" group used to + // silently capture positional arguments. + std::vector<std::string> Groups = Options().groups(); + Groups.erase(std::remove(Groups.begin(), Groups.end(), std::string("__hidden__")), Groups.end()); + + Options().set_width(TuiConsoleColumns(80)); + printf("%s\n", Options().help(Groups).c_str()); + + // Append subcommand listing. + size_t MaxNameLen = 0; + for (ZenSubCmdBase* SubCmd : m_SubCommands) + { + MaxNameLen = std::max(MaxNameLen, SubCmd->SubOptions().program().size()); + } + + printf("subcommands:\n"); + for (ZenSubCmdBase* SubCmd : m_SubCommands) + { + printf(" %-*s %s\n", + static_cast<int>(MaxNameLen), + SubCmd->SubOptions().program().c_str(), + std::string(SubCmd->Description()).c_str()); + } + printf("\nFor global options run: zen --help\n"); +} + +void ZenCmdWithSubCommands::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { std::vector<cxxopts::Options*> SubOptionPtrs; @@ -226,15 +256,47 @@ ZenCmdWithSubCommands::Run(const ZenCliOptions& GlobalOptions, int argc, char** std::vector<char*> SubCommandArguments; int ParentArgc = GetSubCommand(Options(), argc, argv, SubOptionPtrs, MatchedSubOption, SubCommandArguments); - if (!ParseOptions(Options(), ParentArgc, argv)) + // Intercept --help/-h in the parent arg range before calling ParseOptions so + // we can append subcommand information to the output. When a subcommand was + // found argv[ParentArgc-1] is the subcommand name itself, which we exclude. + int ParentArgEnd = (MatchedSubOption != nullptr) ? ParentArgc - 1 : ParentArgc; + for (int i = 1; i < ParentArgEnd; ++i) { - return; + std::string_view Arg(argv[i]); + if (Arg == "--help" || Arg == "-h") + { + PrintHelp(); + return; + } + } + + // Parse parent options. When a subcommand was matched we strip its name from + // the arg list so the parent parser does not see it as an unmatched positional. + if (MatchedSubOption != nullptr) + { + std::vector<char*> ParentArgs; + ParentArgs.reserve(static_cast<size_t>(ParentArgc - 1)); + ParentArgs.push_back(argv[0]); + std::copy(argv + 1, argv + ParentArgc - 1, std::back_inserter(ParentArgs)); + if (!ParseOptions(Options(), static_cast<int>(ParentArgs.size()), ParentArgs.data())) + { + return; + } + } + else + { + if (!ParseOptions(Options(), ParentArgc, argv)) + { + return; + } } if (MatchedSubOption == nullptr) { + PrintHelp(); + ExtendableStringBuilder<128> VerbList; - for (bool First = true; ZenSubCmdBase * SubCmd : m_SubCommands) + for (bool First = true; ZenSubCmdBase* SubCmd : m_SubCommands) { if (!First) { @@ -243,7 +305,7 @@ ZenCmdWithSubCommands::Run(const ZenCliOptions& GlobalOptions, int argc, char** VerbList.Append(SubCmd->SubOptions().program()); First = false; } - throw OptionParseException(fmt::format("No subcommand specified. Available subcommands: {}", VerbList.ToView()), Options().help()); + throw OptionParseException(fmt::format("No subcommand specified. Available subcommands: {}", VerbList.ToView()), {}); } ZenSubCmdBase* MatchedSubCmd = nullptr; @@ -621,6 +683,9 @@ main(int argc, char** argv) Options.add_options()("malloc", "Configure memory allocator subsystem", cxxopts::value(MemoryOptions)->default_value("mimalloc")); Options.add_options()("help", "Show command line help"); Options.add_options()("c, command", "Sub command", cxxopts::value<std::string>(SubCommand)); + Options.add_options()("httpclient", + "Select HTTP client implementation (e.g. 'curl', 'cpr')", + cxxopts::value<std::string>(GlobalOptions.HttpClientBackend)->default_value("cpr")); int CoreLimit = 0; @@ -783,6 +848,8 @@ main(int argc, char** argv) FreeCallstack(Callstack); }); + zen::SetDefaultHttpClientBackend(GlobalOptions.HttpClientBackend); + zen::MaximizeOpenFileCount(); ////////////////////////////////////////////////////////////////////////// |