aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/testing.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-03-10 17:27:26 +0100
committerGitHub Enterprise <[email protected]>2026-03-10 17:27:26 +0100
commitd0a07e555577dcd4a8f55f1b45d9e8e4e6366ab7 (patch)
tree2dfe1e3e0b620043d358e0b7f8bdf8320d985491 /src/zencore/testing.cpp
parentchangelog entry which was inadvertently omitted from PR merge (diff)
downloadzen-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.cpp115
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;