aboutsummaryrefslogtreecommitdiff
path: root/zenserver/cache/structuredcache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'zenserver/cache/structuredcache.cpp')
-rw-r--r--zenserver/cache/structuredcache.cpp177
1 files changed, 152 insertions, 25 deletions
diff --git a/zenserver/cache/structuredcache.cpp b/zenserver/cache/structuredcache.cpp
index eed7a4420..691da36fa 100644
--- a/zenserver/cache/structuredcache.cpp
+++ b/zenserver/cache/structuredcache.cpp
@@ -121,47 +121,148 @@ HttpStructuredCacheService::Scrub(ScrubContext& Ctx)
m_CacheStore.Scrub(Ctx);
}
-void
-HttpStructuredCacheService::HandleRequest(HttpServerRequest& Request)
+static constexpr std::string_view HttpZCacheAPIV2Prefix = "api/v2/"sv;
+static constexpr std::string_view HttpZCacheRPCPrefix = "$rpc"sv;
+
+struct HttpRequestData
{
- CacheRef Ref;
+ std::optional<std::string> Namespace;
+ std::optional<std::string> Bucket;
+ std::optional<IoHash> HashKey;
+ std::optional<IoHash> ValueContentId;
+};
- metrics::OperationTiming::Scope $(m_HttpRequests);
+static bool
+HttpRequestParseRelativeUri(std::string_view Key, HttpRequestData& Data)
+{
+ std::string_view Namespace = ZenCacheStore::DefaultNamespace;
+ if (Key.starts_with(HttpZCacheAPIV2Prefix))
+ {
+ std::string_view::size_type NamespaceSplitOffset = Key.find_first_of('/', HttpZCacheAPIV2Prefix.length());
+ if (NamespaceSplitOffset == std::string_view::npos)
+ {
+ // Namespace reference
+ if (!std::all_of(begin(Key), end(Key), [](const char c) { return std::isalnum(c); }))
+ {
+ return false;
+ }
+ Data.Namespace = ToLower(Key);
+ return true;
+ }
+ Data.Namespace = Key.substr(0, NamespaceSplitOffset);
+ Key = Key.substr(NamespaceSplitOffset + 1);
+ }
+
+ std::string_view::size_type BucketSplitOffset = Key.find_first_of('/');
+ if (BucketSplitOffset == std::string_view::npos)
+ {
+ if (!std::all_of(begin(Key), end(Key), [](const char c) { return std::isalnum(c); }))
+ {
+ return false;
+ }
+ Data.Bucket = ToLower(Key);
+ return true;
+ }
+
+ std::string_view HashSegment;
+ std::string_view ValueSegment;
+
+ std::string_view::size_type ValueSplitOffset = Key.find_last_of('/');
+ if (ValueSplitOffset == BucketSplitOffset)
+ {
+ // Basic cache record lookup
+ HashSegment = Key.substr(BucketSplitOffset + 1);
+ }
+ else
+ {
+ // Cache record + valueid lookup
+ HashSegment = Key.substr(BucketSplitOffset + 1, ValueSplitOffset - BucketSplitOffset - 1);
+ ValueSegment = Key.substr(ValueSplitOffset + 1);
+ }
- if (!ValidateKeyUri(Request, /* out */ Ref))
+ if (HashSegment.size() != IoHash::StringLength)
{
- std::string_view Key = Request.RelativeUri();
+ return false;
+ }
+
+ IoHash KeyHash;
+ if (!ParseHexBytes(HashSegment.data(), HashSegment.size(), KeyHash.Hash))
+ {
+ return false;
+ }
- if (Key == "$rpc")
+ Data.HashKey = KeyHash;
+
+ if (!ValueSegment.empty())
+ {
+ if (ValueSegment.size() != IoHash::StringLength)
{
- return HandleRpcRequest(Request);
+ return false;
}
- if (std::all_of(begin(Key), end(Key), [](const char c) { return std::isalnum(c); }))
+ IoHash ValueHash;
+ if (!ParseHexBytes(ValueSegment.data(), ValueSegment.size(), ValueHash.Hash))
{
- // Bucket reference
- return HandleCacheBucketRequest(Request, Key);
+ return false;
}
+ Data.ValueContentId = ValueHash;
+ }
+
+ return true;
+}
+
+void
+HttpStructuredCacheService::HandleRequest(HttpServerRequest& Request)
+{
+ metrics::OperationTiming::Scope $(m_HttpRequests);
+
+ std::string_view Key = Request.RelativeUri();
+ if (Key == HttpZCacheRPCPrefix)
+ {
+ return HandleRpcRequest(Request);
+ }
+ HttpRequestData RequestData;
+ if (!HttpRequestParseRelativeUri(Key, RequestData))
+ {
return Request.WriteResponse(HttpResponseCode::BadRequest); // invalid URL
}
- CachePolicy PolicyFromURL = ParseCachePolicy(Request.GetQueryParams());
+ if (RequestData.ValueContentId.has_value())
+ {
+ ZEN_ASSERT(RequestData.Namespace.has_value());
+ ZEN_ASSERT(RequestData.Bucket.has_value());
+ ZEN_ASSERT(RequestData.HashKey.has_value());
+ CacheRef Ref = {.Namespace = RequestData.Namespace.value(),
+ .BucketSegment = RequestData.Bucket.value(),
+ .HashKey = RequestData.HashKey.value(),
+ .ValueContentId = RequestData.ValueContentId.value()};
+ return HandleCacheValueRequest(Request, Ref, ParseCachePolicy(Request.GetQueryParams()));
+ }
- if (Ref.ValueContentId == IoHash::Zero)
+ if (RequestData.HashKey.has_value())
{
- return HandleCacheRecordRequest(Request, Ref, PolicyFromURL);
+ ZEN_ASSERT(RequestData.Namespace.has_value());
+ ZEN_ASSERT(RequestData.Bucket.has_value());
+ CacheRef Ref = {.Namespace = RequestData.Namespace.value(),
+ .BucketSegment = RequestData.Bucket.value(),
+ .HashKey = RequestData.HashKey.value(),
+ .ValueContentId = IoHash::Zero};
+ return HandleCacheRecordRequest(Request, Ref, ParseCachePolicy(Request.GetQueryParams()));
}
- else
+
+ if (RequestData.Bucket.has_value())
{
- return HandleCacheValueRequest(Request, Ref, PolicyFromURL);
+ ZEN_ASSERT(RequestData.Namespace.has_value());
+ return HandleCacheBucketRequest(Request, RequestData.Namespace.value(), RequestData.Bucket.value());
}
- return;
+ ZEN_ASSERT(RequestData.Namespace.has_value());
+ return HandleCacheNamespaceRequest(Request, RequestData.Namespace.value());
}
void
-HttpStructuredCacheService::HandleCacheBucketRequest(HttpServerRequest& Request, std::string_view Bucket)
+HttpStructuredCacheService::HandleCacheNamespaceRequest(zen::HttpServerRequest& Request, std::string_view)
{
switch (Request.RequestVerb())
{
@@ -173,11 +274,39 @@ HttpStructuredCacheService::HandleCacheBucketRequest(HttpServerRequest& Request,
break;
case HttpVerb::kDelete:
- // Drop bucket
+ // Drop namespace
{
- // TODO: Should add namespace to URI and handle if the namespace is missing for backwards compatability
- std::string_view Namespace = ZenCacheStore::DefaultNamespace;
+ // if (m_CacheStore.DropNamespace(Namespace))
+ // {
+ // return Request.WriteResponse(HttpResponseCode::OK);
+ // }
+ // else
+ // {
+ // return Request.WriteResponse(HttpResponseCode::NotFound);
+ // }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void
+HttpStructuredCacheService::HandleCacheBucketRequest(HttpServerRequest& Request, std::string_view Namespace, std::string_view Bucket)
+{
+ switch (Request.RequestVerb())
+ {
+ case HttpVerb::kHead:
+ case HttpVerb::kGet:
+ {
+ // Query stats
+ }
+ break;
+
+ case HttpVerb::kDelete:
+ // Drop bucket
+ {
if (m_CacheStore.DropBucket(Namespace, Bucket))
{
return Request.WriteResponse(HttpResponseCode::OK);
@@ -783,9 +912,8 @@ HttpStructuredCacheService::HandlePutCacheValue(zen::HttpServerRequest& Request,
}
bool
-HttpStructuredCacheService::ValidateKeyUri(HttpServerRequest& Request, CacheRef& OutRef)
+HttpStructuredCacheService::ValidateKeyUri(std::string_view Namespace, std::string_view Key, CacheRef& OutRef)
{
- std::string_view Key = Request.RelativeUri();
std::string_view::size_type BucketSplitOffset = Key.find_first_of('/');
if (BucketSplitOffset == std::string_view::npos)
@@ -793,8 +921,7 @@ HttpStructuredCacheService::ValidateKeyUri(HttpServerRequest& Request, CacheRef&
return false;
}
- OutRef.Namespace = ToLower(ZenCacheStore::DefaultNamespace); // TODO: Should add namespace to URI and handle if the namespace is
- // missing for backwards compatability
+ OutRef.Namespace = ToLower(Namespace);
OutRef.BucketSegment = ToLower(Key.substr(0, BucketSplitOffset));
if (!std::all_of(begin(OutRef.Namespace), end(OutRef.Namespace), [](const char c) { return std::isalnum(c); }))