// tools/bench/support/local_server.hpp — spawn a fast static-file HTTP server for the // throughput/load benches. // // tools/testserver's Python server is right for the hostile-mode correctness suite, but // its body generation hashes every chunk in Python, which bottlenecks well below anything // resembling a saturated link — a throughput number measured against it would be measuring // the test server, not the engine. `busybox httpd` is a small C static file server that // supports Range/If-Range/ETag properly (verified: 206 + Content-Range + ETag + // Last-Modified + Accept-Ranges on a ranged GET) and comes with coreutils on most Linux // boxes, so it stands in for "a plain, honest, reasonably fast origin" without pulling in // nginx or writing our own. // // Linux-only (fork/exec/waitpid), like tools/testserver's fixture. If busybox is missing // or every candidate port is taken, available() is false and the caller should skip. #ifndef VDM_BENCH_LOCAL_SERVER_HPP #define VDM_BENCH_LOCAL_SERVER_HPP #include #include #include #include #include #include #include #include #include namespace vdm::bench { class LocalServer { public: // Serves `docroot` over HTTP on 127.0.0.1. Tries a handful of pseudo-random high ports // (busybox exits(1) with "bind: Address already in use" on a taken one; there's no // ephemeral-port mode to ask it for one back, unlike tools/testserver's Python server). explicit LocalServer(std::string docroot) : docroot_(std::move(docroot)) { unsigned seed = static_cast(::getpid()) ^ static_cast(std::chrono::steady_clock::now() .time_since_epoch() .count()); std::srand(seed); for (int attempt = 0; attempt < 8 && pid_ < 0; ++attempt) { int candidate = 20000 + (std::rand() % 40000); if (try_start(candidate)) port_ = candidate; } } ~LocalServer() { if (pid_ > 0) { ::kill(pid_, SIGTERM); int status = 0; for (int i = 0; i < 50; ++i) { if (::waitpid(pid_, &status, WNOHANG) == pid_) return; std::this_thread::sleep_for(std::chrono::milliseconds(20)); } ::kill(pid_, SIGKILL); ::waitpid(pid_, &status, 0); } } LocalServer(const LocalServer &) = delete; LocalServer &operator=(const LocalServer &) = delete; [[nodiscard]] bool available() const { return pid_ > 0; } [[nodiscard]] std::string url(const std::string &path) const { return "http://127.0.0.1:" + std::to_string(port_) + path; } private: bool try_start(int port) { pid_t pid = ::fork(); if (pid < 0) return false; if (pid == 0) { int devnull = ::open("/dev/null", O_WRONLY); if (devnull >= 0) { ::dup2(devnull, STDOUT_FILENO); ::dup2(devnull, STDERR_FILENO); } ::execlp("busybox", "busybox", "httpd", "-f", "-p", std::to_string(port).c_str(), "-h", docroot_.c_str(), static_cast(nullptr)); ::_exit(127); // busybox not found } // Give it a moment to either bind-and-block (success) or bind-fail-and-exit. std::this_thread::sleep_for(std::chrono::milliseconds(150)); int status = 0; pid_t r = ::waitpid(pid, &status, WNOHANG); if (r == pid) return false; // already exited: port taken, or busybox missing pid_ = pid; return true; } std::string docroot_; pid_t pid_ = -1; int port_ = 0; }; // Create (or truncate to) a file of `bytes` length without writing them: content is // whatever the filesystem hands back for a hole (zeros), which is fine for a throughput // measurement — we're timing the transfer and write path, not verifying content. [[nodiscard]] inline bool make_sparse_file(const std::string &path, std::uint64_t bytes) { int fd = ::open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) return false; bool ok = ::ftruncate(fd, static_cast(bytes)) == 0; ::close(fd); return ok; } } // namespace vdm::bench #endif // VDM_BENCH_LOCAL_SERVER_HPP