diff options
| author | Stefan Boberg <[email protected]> | 2026-03-27 16:25:21 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-03-27 16:25:21 +0100 |
| commit | 86c95e3291542433190e48fd7e7ad0a414644bc1 (patch) | |
| tree | c8c05200e2889a4c4bc87cbd3fd47ee3021bc24f /src | |
| parent | 5.8.0-pre0 (diff) | |
| download | zen-86c95e3291542433190e48fd7e7ad0a414644bc1.tar.xz zen-86c95e3291542433190e48fd7e7ad0a414644bc1.zip | |
Misc small fixes (#897)
- **Eliminate `<regex>` usage** — Replaced `std::regex`-based URL parsing in `jupiterbuildstorage.cpp` with manual `string_view` parsing. Added `CXXOPTS_NO_REGEX` to disable regex in cxxopts. Includes comprehensive tests for the new URL parser.
- **Add missing HTTP response codes** — Added `102`, `103`, `203`, `207`, `208`, `226`, `306`, `421`, `425`, `451` to the enum and reason string lookup.
- **Add `ForceColor` support to zen CLI** — Plumbed the `ForceColor` logging option through to the zen client.
- **Add `.clangd` config** — Strips MSVC-specific flags clangd can't handle and suppresses noisy clang-tidy checks.
- **Generic `fmt::formatter` for `ToString`** — Concept-based formatter that auto-formats any type with a free `ToString()` function, removing the need for per-type specializations.
- **Fix OpenSSL dependency** — Changed `zenhorde` to use `openssl3` package on Linux/macOS.
- **Add `<cmath>` include** — Missing include in `hyperloglog.h`.
- **GCC compile fix** — Moved `static constinit` variable inside lambda in `logging.cpp`.
Diffstat (limited to 'src')
| -rw-r--r-- | src/zen/zen.cpp | 1 | ||||
| -rw-r--r-- | src/zencore/include/zencore/fmtutils.h | 23 | ||||
| -rw-r--r-- | src/zenhorde/xmake.lua | 2 | ||||
| -rw-r--r-- | src/zenhttp/httpserver.cpp | 14 | ||||
| -rw-r--r-- | src/zenhttp/include/zenhttp/httpcommon.h | 3 | ||||
| -rw-r--r-- | src/zenremotestore/builds/jupiterbuildstorage.cpp | 144 | ||||
| -rw-r--r-- | src/zenremotestore/include/zenremotestore/builds/jupiterbuildstorage.h | 2 | ||||
| -rw-r--r-- | src/zenremotestore/zenremotestore.cpp | 2 | ||||
| -rw-r--r-- | src/zenserver/diag/logging.cpp | 2 | ||||
| -rw-r--r-- | src/zentelemetry/include/zentelemetry/hyperloglog.h | 1 |
10 files changed, 170 insertions, 24 deletions
diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp index 5f92a7ca3..3277eb856 100644 --- a/src/zen/zen.cpp +++ b/src/zen/zen.cpp @@ -949,6 +949,7 @@ main(int argc, char** argv) .IsTest = false, .NoConsoleOutput = GlobalOptions.LoggingConfig.NoConsoleOutput, .QuietConsole = GlobalOptions.LoggingConfig.QuietConsole, + .ForceColor = GlobalOptions.LoggingConfig.ForceColor, .AbsLogFile = GlobalOptions.LoggingConfig.AbsLogFile, .LogId = GlobalOptions.LoggingConfig.LogId}; zen::InitializeLogging(LogOptions); diff --git a/src/zencore/include/zencore/fmtutils.h b/src/zencore/include/zencore/fmtutils.h index 404e570fd..4ec05f901 100644 --- a/src/zencore/include/zencore/fmtutils.h +++ b/src/zencore/include/zencore/fmtutils.h @@ -15,6 +15,29 @@ ZEN_THIRD_PARTY_INCLUDES_END #include <chrono> #include <string_view> +// Generic formatter for any type with a free ToString(T) function returning a +// string-like type. This covers enum-to-string conversions (HttpResponseCode, +// SessionState, etc.) without needing per-type fmt::formatter specializations. +// ADL is used to find ToString, so it works across namespaces. + +template<typename T> +concept HasFreeToString = requires(const T& v) +{ + { + ToString(v) + } -> std::convertible_to<std::string_view>; +}; + +template<HasFreeToString T> +struct fmt::formatter<T> : fmt::formatter<std::string_view> +{ + template<typename FormatContext> + auto format(const T& Value, FormatContext& Ctx) const + { + return fmt::formatter<std::string_view>::format(ToString(Value), Ctx); + } +}; + // Custom formatting for some zencore types template<typename T> diff --git a/src/zenhorde/xmake.lua b/src/zenhorde/xmake.lua index 48d028e86..0e69e9c5f 100644 --- a/src/zenhorde/xmake.lua +++ b/src/zenhorde/xmake.lua @@ -14,7 +14,7 @@ target('zenhorde') end if is_plat("linux") or is_plat("macosx") then - add_packages("openssl") + add_packages("openssl3") end if is_os("macosx") then diff --git a/src/zenhttp/httpserver.cpp b/src/zenhttp/httpserver.cpp index 9fc42f18c..e05c9815f 100644 --- a/src/zenhttp/httpserver.cpp +++ b/src/zenhttp/httpserver.cpp @@ -329,6 +329,10 @@ ReasonStringForHttpResultCode(int HttpCode) return "Continue"sv; case 101: return "Switching Protocols"sv; + case 102: + return "Processing"sv; + case 103: + return "Early Hints"sv; // 2xx Success @@ -338,12 +342,20 @@ ReasonStringForHttpResultCode(int HttpCode) return "Created"sv; case 202: return "Accepted"sv; + case 203: + return "Non-Authoritative Information"sv; case 204: return "No Content"sv; case 205: return "Reset Content"sv; case 206: return "Partial Content"sv; + case 207: + return "Multi-Status"sv; + case 208: + return "Already Reported"sv; + case 226: + return "IM Used"sv; // 3xx Redirection @@ -424,6 +436,8 @@ ReasonStringForHttpResultCode(int HttpCode) return "Too Many Requests"sv; case 431: return "Request Header Fields Too Large"sv; + case 451: + return "Unavailable For Legal Reasons"sv; // 5xx Server errors diff --git a/src/zenhttp/include/zenhttp/httpcommon.h b/src/zenhttp/include/zenhttp/httpcommon.h index 8fca35ac5..f9a99f3cc 100644 --- a/src/zenhttp/include/zenhttp/httpcommon.h +++ b/src/zenhttp/include/zenhttp/httpcommon.h @@ -91,6 +91,7 @@ enum class HttpResponseCode //!< were not for the fact that the condition has evaluated to false. UseProxy = 305, //!< \deprecated \parblock Due to security concerns regarding in-band configuration of a proxy. \endparblock //!< The requested resource MUST be accessed through the proxy given by the Location field. + SwitchProxy = 306, //!< \deprecated No longer used. Originally meant subsequent requests should use the specified proxy. TemporaryRedirect = 307, //!< Indicates that the target resource resides temporarily under a different URI and the user agent MUST NOT //!< change the request method if it performs an automatic redirection to that URI. PermanentRedirect = 308, //!< The target resource has been assigned a new permanent URI and any future references to this resource @@ -133,12 +134,14 @@ enum class HttpResponseCode ExpectationFailed = 417, //!< Indicates that the expectation given in the request's Expect header field could not be met by at least //!< one of the inbound servers. ImATeapot = 418, //!< Any attempt to brew coffee with a teapot should result in the error code 418 I'm a teapot. + MisdirectedRequest = 421, //!< Indicates that the request was directed at a server that is not able to produce a response. UnprocessableEntity = 422, //!< Means the server understands the content type of the request entity (hence a 415(Unsupported Media //!< Type) status code is inappropriate), and the syntax of the request entity is correct (thus a 400 (Bad //!< Request) status code is inappropriate) but was unable to process the contained instructions. Locked = 423, //!< Means the source or destination resource of a method is locked. FailedDependency = 424, //!< Means that the method could not be performed on the resource because the requested action depended on //!< another action and that action failed. + TooEarly = 425, //!< Indicates that the server is unwilling to risk processing a request that might be replayed. UpgradeRequired = 426, //!< Indicates that the server refuses to perform the request using the current protocol but might be willing to //!< do so after the client upgrades to a different protocol. PreconditionRequired = 428, //!< Indicates that the origin server requires the request to be conditional. diff --git a/src/zenremotestore/builds/jupiterbuildstorage.cpp b/src/zenremotestore/builds/jupiterbuildstorage.cpp index c3f7b9e71..ad4c4bc89 100644 --- a/src/zenremotestore/builds/jupiterbuildstorage.cpp +++ b/src/zenremotestore/builds/jupiterbuildstorage.cpp @@ -14,7 +14,7 @@ ZEN_THIRD_PARTY_INCLUDES_START #include <tsl/robin_map.h> ZEN_THIRD_PARTY_INCLUDES_END -#include <regex> +#include <string_view> namespace zen { @@ -572,35 +572,135 @@ ParseBuildStorageUrl(std::string_view InUrl, Url.erase(ApiString, ExtendedApiString.length()); } - const std::string ArtifactURLRegExString = R"((http[s]?:\/\/.*?)\/(.*?)\/(.*?)\/(.*))"; - const std::regex ArtifactURLRegEx(ArtifactURLRegExString, std::regex::ECMAScript | std::regex::icase); - std::match_results<std::string_view::const_iterator> MatchResults; - std::string_view UrlToParse(Url); - if (regex_match(begin(UrlToParse), end(UrlToParse), MatchResults, ArtifactURLRegEx) && MatchResults.size() == 5) - { - auto GetMatch = [&MatchResults](uint32_t Index) -> std::string_view { - ZEN_ASSERT(Index < MatchResults.size()); + // Parse URL of the form: http[s]://host/namespace/bucket/buildid + std::string_view Remaining(Url); - const auto& Match = MatchResults[Index]; + // Find the end of the scheme (e.g. "http://" or "https://") + size_t SchemeEnd = Remaining.find("://"); + if (SchemeEnd == std::string_view::npos) + { + return false; + } + SchemeEnd += 3; // skip past "://" - return std::string_view(&*Match.first, Match.second - Match.first); - }; + // Find the first '/' after the host + size_t HostEnd = Remaining.find('/', SchemeEnd); + if (HostEnd == std::string_view::npos) + { + return false; + } - const std::string_view Host = GetMatch(1); - const std::string_view Namespace = GetMatch(2); - const std::string_view Bucket = GetMatch(3); - const std::string_view BuildId = GetMatch(4); + // Find the '/' after namespace + size_t NamespaceEnd = Remaining.find('/', HostEnd + 1); + if (NamespaceEnd == std::string_view::npos) + { + return false; + } - OutHost = Host; - OutNamespace = Namespace; - OutBucket = Bucket; - OutBuildId = BuildId; - return true; + // Find the '/' after bucket + size_t BucketEnd = Remaining.find('/', NamespaceEnd + 1); + if (BucketEnd == std::string_view::npos) + { + return false; } - else + + // BuildId must be non-empty + if (BucketEnd + 1 >= Remaining.size()) { return false; } + + OutHost = Remaining.substr(0, HostEnd); + OutNamespace = Remaining.substr(HostEnd + 1, NamespaceEnd - HostEnd - 1); + OutBucket = Remaining.substr(NamespaceEnd + 1, BucketEnd - NamespaceEnd - 1); + OutBuildId = Remaining.substr(BucketEnd + 1); + return true; +} + +} // namespace zen + +#if ZEN_WITH_TESTS + +# include <zencore/testing.h> + +namespace zen { + +void +jupiterbuildstorage_forcelink() +{ } } // namespace zen + +TEST_SUITE_BEGIN("remotestore.jupiterbuildstorage"); + +TEST_CASE("ParseBuildStorageUrl.ValidUrl") +{ + std::string Host, Namespace, Bucket, BuildId; + bool Result = + zen::ParseBuildStorageUrl("https://horde.devtools.epicgames.com/mynamespace/mybucket/mybuildid", Host, Namespace, Bucket, BuildId); + CHECK(Result); + CHECK(Host == "https://horde.devtools.epicgames.com"); + CHECK(Namespace == "mynamespace"); + CHECK(Bucket == "mybucket"); + CHECK(BuildId == "mybuildid"); +} + +TEST_CASE("ParseBuildStorageUrl.ValidUrlWithApiPrefix") +{ + std::string Host, Namespace, Bucket, BuildId; + bool Result = zen::ParseBuildStorageUrl("https://horde.devtools.epicgames.com/api/v2/builds/mynamespace/mybucket/mybuildid", + Host, + Namespace, + Bucket, + BuildId); + CHECK(Result); + CHECK(Host == "https://horde.devtools.epicgames.com"); + CHECK(Namespace == "mynamespace"); + CHECK(Bucket == "mybucket"); + CHECK(BuildId == "mybuildid"); +} + +TEST_CASE("ParseBuildStorageUrl.HttpScheme") +{ + std::string Host, Namespace, Bucket, BuildId; + bool Result = zen::ParseBuildStorageUrl("http://localhost/ns/bucket/build123", Host, Namespace, Bucket, BuildId); + CHECK(Result); + CHECK(Host == "http://localhost"); + CHECK(Namespace == "ns"); + CHECK(Bucket == "bucket"); + CHECK(BuildId == "build123"); +} + +TEST_CASE("ParseBuildStorageUrl.BuildIdWithSlashes") +{ + std::string Host, Namespace, Bucket, BuildId; + bool Result = zen::ParseBuildStorageUrl("https://host/ns/bucket/build/with/slashes", Host, Namespace, Bucket, BuildId); + CHECK(Result); + CHECK(Host == "https://host"); + CHECK(Namespace == "ns"); + CHECK(Bucket == "bucket"); + CHECK(BuildId == "build/with/slashes"); +} + +TEST_CASE("ParseBuildStorageUrl.MissingBuildId") +{ + std::string Host, Namespace, Bucket, BuildId; + CHECK_FALSE(zen::ParseBuildStorageUrl("https://host/ns/bucket/", Host, Namespace, Bucket, BuildId)); +} + +TEST_CASE("ParseBuildStorageUrl.MissingBucket") +{ + std::string Host, Namespace, Bucket, BuildId; + CHECK_FALSE(zen::ParseBuildStorageUrl("https://host/ns", Host, Namespace, Bucket, BuildId)); +} + +TEST_CASE("ParseBuildStorageUrl.NoScheme") +{ + std::string Host, Namespace, Bucket, BuildId; + CHECK_FALSE(zen::ParseBuildStorageUrl("host/ns/bucket/buildid", Host, Namespace, Bucket, BuildId)); +} + +TEST_SUITE_END(); + +#endif // ZEN_WITH_TESTS diff --git a/src/zenremotestore/include/zenremotestore/builds/jupiterbuildstorage.h b/src/zenremotestore/include/zenremotestore/builds/jupiterbuildstorage.h index 888ec8ead..270835521 100644 --- a/src/zenremotestore/include/zenremotestore/builds/jupiterbuildstorage.h +++ b/src/zenremotestore/include/zenremotestore/builds/jupiterbuildstorage.h @@ -22,4 +22,6 @@ bool ParseBuildStorageUrl(std::string_view InUrl, std::string& OutBucket, std::string& OutBuildId); +void jupiterbuildstorage_forcelink(); + } // namespace zen diff --git a/src/zenremotestore/zenremotestore.cpp b/src/zenremotestore/zenremotestore.cpp index a0bb17260..0b205b296 100644 --- a/src/zenremotestore/zenremotestore.cpp +++ b/src/zenremotestore/zenremotestore.cpp @@ -5,6 +5,7 @@ #include <zenremotestore/builds/buildmanifest.h> #include <zenremotestore/builds/buildsavedstate.h> #include <zenremotestore/builds/buildstorageoperations.h> +#include <zenremotestore/builds/jupiterbuildstorage.h> #include <zenremotestore/chunking/chunkedcontent.h> #include <zenremotestore/chunking/chunkedfile.h> #include <zenremotestore/chunking/chunkingcache.h> @@ -20,6 +21,7 @@ zenremotestore_forcelinktests() { buildmanifest_forcelink(); buildsavedstate_forcelink(); + jupiterbuildstorage_forcelink(); buildstorageoperations_forcelink(); chunkblock_forcelink(); chunkedcontent_forcelink(); diff --git a/src/zenserver/diag/logging.cpp b/src/zenserver/diag/logging.cpp index 7513e56f7..f3d8dbfe3 100644 --- a/src/zenserver/diag/logging.cpp +++ b/src/zenserver/diag/logging.cpp @@ -111,8 +111,8 @@ InitializeServerLogging(const ZenServerConfig& InOptions, bool WithCacheService) const zen::Oid ServerSessionId = zen::GetSessionId(); - static constinit logging::LogPoint SessionIdPoint{{}, logging::Info, "server session id: {}"}; logging::Registry::Instance().ApplyAll([&](auto Logger) { + static constinit logging::LogPoint SessionIdPoint{{}, logging::Info, "server session id: {}"}; ZEN_MEMSCOPE(ELLMTag::Logging); Logger->Log(SessionIdPoint, fmt::make_format_args(ServerSessionId)); }); diff --git a/src/zentelemetry/include/zentelemetry/hyperloglog.h b/src/zentelemetry/include/zentelemetry/hyperloglog.h index 2daf75a43..502e2aee5 100644 --- a/src/zentelemetry/include/zentelemetry/hyperloglog.h +++ b/src/zentelemetry/include/zentelemetry/hyperloglog.h @@ -9,6 +9,7 @@ #include <array> #include <atomic> +#include <cmath> #include <cstdint> #include <string_view> |