aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-03-27 16:25:21 +0100
committerGitHub Enterprise <[email protected]>2026-03-27 16:25:21 +0100
commit86c95e3291542433190e48fd7e7ad0a414644bc1 (patch)
treec8c05200e2889a4c4bc87cbd3fd47ee3021bc24f /src
parent5.8.0-pre0 (diff)
downloadzen-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.cpp1
-rw-r--r--src/zencore/include/zencore/fmtutils.h23
-rw-r--r--src/zenhorde/xmake.lua2
-rw-r--r--src/zenhttp/httpserver.cpp14
-rw-r--r--src/zenhttp/include/zenhttp/httpcommon.h3
-rw-r--r--src/zenremotestore/builds/jupiterbuildstorage.cpp144
-rw-r--r--src/zenremotestore/include/zenremotestore/builds/jupiterbuildstorage.h2
-rw-r--r--src/zenremotestore/zenremotestore.cpp2
-rw-r--r--src/zenserver/diag/logging.cpp2
-rw-r--r--src/zentelemetry/include/zentelemetry/hyperloglog.h1
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>