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/zencore/testing.cpp | |
| parent | changelog entry which was inadvertently omitted from PR merge (diff) | |
| download | zen-d0a07e555577dcd4a8f55f1b45d9e8e4e6366ab7.tar.xz 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/zencore/testing.cpp')
| -rw-r--r-- | src/zencore/testing.cpp | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/src/zencore/testing.cpp b/src/zencore/testing.cpp index 089e376bb..d7eb3b17d 100644 --- a/src/zencore/testing.cpp +++ b/src/zencore/testing.cpp @@ -11,17 +11,131 @@ #if ZEN_WITH_TESTS +# include <zencore/callstack.h> + # include <chrono> # include <clocale> +# include <csignal> # include <cstdlib> # include <cstdio> # include <string> # include <vector> +# if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC +# include <execinfo.h> +# include <unistd.h> +# endif + namespace zen::testing { using namespace std::literals; +static void +PrintCrashCallstack([[maybe_unused]] const char* SignalName) +{ +# if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC + // Use write() + backtrace_symbols_fd() which are async-signal-safe + write(STDERR_FILENO, "\n*** Caught ", 12); + write(STDERR_FILENO, SignalName, strlen(SignalName)); + write(STDERR_FILENO, " — callstack:\n", 15); + + void* Frames[64]; + int FrameCount = backtrace(Frames, 64); + backtrace_symbols_fd(Frames, FrameCount, STDERR_FILENO); +# elif ZEN_PLATFORM_WINDOWS + // On Windows we're called from SEH, not a signal handler, so heap/locks are safe + void* Addresses[64]; + uint32_t FrameCount = GetCallstack(2, 64, Addresses); + if (FrameCount > 0) + { + std::vector<std::string> Symbols = GetFrameSymbols(FrameCount, Addresses); + fprintf(stderr, "\n*** Caught %s - callstack:\n", SignalName); + for (uint32_t i = 0; i < FrameCount; ++i) + { + fprintf(stderr, " %2u: %s\n", i, Symbols[i].c_str()); + } + } +# endif +} + +# if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC + +static void +CrashSignalHandler(int Signal) +{ + const char* SignalName = "Unknown signal"; + switch (Signal) + { + case SIGSEGV: + SignalName = "SIGSEGV"; + break; + case SIGABRT: + SignalName = "SIGABRT"; + break; + case SIGFPE: + SignalName = "SIGFPE"; + break; + case SIGBUS: + SignalName = "SIGBUS"; + break; + case SIGILL: + SignalName = "SIGILL"; + break; + } + + PrintCrashCallstack(SignalName); + + // Re-raise with default handler so the process terminates normally + signal(Signal, SIG_DFL); + raise(Signal); +} + +# endif // ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC + +# if ZEN_PLATFORM_WINDOWS + +static LONG CALLBACK +CrashVectoredHandler(PEXCEPTION_POINTERS ExceptionInfo) +{ + // Only handle fatal exceptions, not first-chance exceptions used for normal control flow + switch (ExceptionInfo->ExceptionRecord->ExceptionCode) + { + case EXCEPTION_ACCESS_VIOLATION: + PrintCrashCallstack("EXCEPTION_ACCESS_VIOLATION"); + break; + case EXCEPTION_STACK_OVERFLOW: + PrintCrashCallstack("EXCEPTION_STACK_OVERFLOW"); + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + PrintCrashCallstack("EXCEPTION_ILLEGAL_INSTRUCTION"); + break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + PrintCrashCallstack("EXCEPTION_INT_DIVIDE_BY_ZERO"); + break; + default: + break; + } + + // Continue search so doctest's handler can report the test case context + return EXCEPTION_CONTINUE_SEARCH; +} + +# endif // ZEN_PLATFORM_WINDOWS + +static void +InstallCrashSignalHandlers() +{ +# if ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC + signal(SIGSEGV, CrashSignalHandler); + signal(SIGABRT, CrashSignalHandler); + signal(SIGFPE, CrashSignalHandler); + signal(SIGBUS, CrashSignalHandler); + signal(SIGILL, CrashSignalHandler); +# elif ZEN_PLATFORM_WINDOWS + AddVectoredExceptionHandler(0 /*called last among vectored handlers*/, CrashVectoredHandler); +# endif +} + struct TestListener : public doctest::IReporter { const std::string_view ColorYellow = "\033[0;33m"sv; @@ -184,6 +298,7 @@ RunTestMain(int Argc, char* Argv[], const char* ExecutableName, void (*ForceLink zen::logging::InitializeLogging(); zen::MaximizeOpenFileCount(); + InstallCrashSignalHandlers(); TestRunner Runner; |