// Copyright Epic Games, Inc. All Rights Reserved. #include #include namespace zen { namespace { constinit AsciiSet ValidNamespaceNameCharactersSet{"abcdefghijklmnopqrstuvwxyz0123456789-_.ABCDEFGHIJKLMNOPQRSTUVWXYZ"}; constinit AsciiSet ValidBucketNameCharactersSet{"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"}; } // namespace std::optional GetValidNamespaceName(std::string_view Name) { if (Name.empty()) { ZEN_WARN("Namespace is invalid, empty namespace is not allowed"); return {}; } if (Name.length() > 64) { ZEN_WARN("Namespace '{}' is invalid, length exceeds 64 characters", Name); return {}; } if (!AsciiSet::HasOnly(Name, ValidNamespaceNameCharactersSet)) { ZEN_WARN("Namespace '{}' is invalid, invalid characters detected", Name); return {}; } return ToLower(Name); } std::optional GetValidBucketName(std::string_view Name) { if (Name.empty()) { ZEN_WARN("Bucket name is invalid, empty bucket name is not allowed"); return {}; } if (!AsciiSet::HasOnly(Name, ValidBucketNameCharactersSet)) { ZEN_WARN("Bucket name '{}' is invalid, invalid characters detected", Name); return {}; } return ToLower(Name); } std::optional GetValidIoHash(std::string_view Hash) { if (Hash.length() != IoHash::StringLength) { return {}; } IoHash KeyHash; if (!ParseHexBytes(Hash.data(), Hash.size(), KeyHash.Hash)) { return {}; } return KeyHash; } bool HttpCacheRequestParseRelativeUri(std::string_view Key, std::string_view DefaultNamespace, HttpCacheRequestData& Data) { std::vector Tokens; uint32_t TokenCount = ForEachStrTok(Key, '/', [&](const std::string_view& Token) { Tokens.push_back(Token); return true; }); switch (TokenCount) { case 0: return true; case 1: Data.Namespace = GetValidNamespaceName(Tokens[0]); return Data.Namespace.has_value(); case 2: { std::optional PossibleHashKey = GetValidIoHash(Tokens[1]); if (PossibleHashKey.has_value()) { // Legacy bucket/key request Data.Bucket = GetValidBucketName(Tokens[0]); if (!Data.Bucket.has_value()) { return false; } Data.HashKey = PossibleHashKey; Data.Namespace = DefaultNamespace; return true; } Data.Namespace = GetValidNamespaceName(Tokens[0]); if (!Data.Namespace.has_value()) { return false; } Data.Bucket = GetValidBucketName(Tokens[1]); if (!Data.Bucket.has_value()) { return false; } return true; } case 3: { std::optional PossibleHashKey = GetValidIoHash(Tokens[1]); if (PossibleHashKey.has_value()) { // Legacy bucket/key/valueid request Data.Bucket = GetValidBucketName(Tokens[0]); if (!Data.Bucket.has_value()) { return false; } Data.HashKey = PossibleHashKey; Data.ValueContentId = GetValidIoHash(Tokens[2]); if (!Data.ValueContentId.has_value()) { return false; } Data.Namespace = DefaultNamespace; return true; } Data.Namespace = GetValidNamespaceName(Tokens[0]); if (!Data.Namespace.has_value()) { return false; } Data.Bucket = GetValidBucketName(Tokens[1]); if (!Data.Bucket.has_value()) { return false; } Data.HashKey = GetValidIoHash(Tokens[2]); if (!Data.HashKey) { return false; } return true; } case 4: { Data.Namespace = GetValidNamespaceName(Tokens[0]); if (!Data.Namespace.has_value()) { return false; } Data.Bucket = GetValidBucketName(Tokens[1]); if (!Data.Bucket.has_value()) { return false; } Data.HashKey = GetValidIoHash(Tokens[2]); if (!Data.HashKey.has_value()) { return false; } Data.ValueContentId = GetValidIoHash(Tokens[3]); if (!Data.ValueContentId.has_value()) { return false; } return true; } default: return false; } } std::optional GetCacheRequestNamespace(const CbObjectView& Params) { CbFieldView NamespaceField = Params["Namespace"]; if (!NamespaceField) { return std::string("!default!"); // ZenCacheStore::DefaultNamespace); } if (NamespaceField.HasError()) { return {}; } if (!NamespaceField.IsString()) { return {}; } return GetValidNamespaceName(NamespaceField.AsString()); } bool GetCacheRequestCacheKey(const CbObjectView& KeyView, CacheKey& Key) { CbFieldView BucketField = KeyView["Bucket"]; if (BucketField.HasError()) { return false; } if (!BucketField.IsString()) { return false; } std::optional Bucket = GetValidBucketName(BucketField.AsString()); if (!Bucket.has_value()) { return false; } CbFieldView HashField = KeyView["Hash"]; if (HashField.HasError()) { return false; } if (!HashField.IsHash()) { return false; } Key.Bucket = *Bucket; Key.Hash = HashField.AsHash(); return true; } } // namespace zen