diff options
| author | Stefan Boberg <[email protected]> | 2025-10-14 11:32:16 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-10-14 11:32:16 +0200 |
| commit | ca09abbeef5b1788f4a52b61eedd2f3dd07f81f2 (patch) | |
| tree | 005a50adfddf6982bab3a06bb93d4c50da1a11fd /src/zenserver/workspaces/httpworkspaces.cpp | |
| parent | make asiohttp work without IPv6 (#562) (diff) | |
| download | zen-ca09abbeef5b1788f4a52b61eedd2f3dd07f81f2.tar.xz zen-ca09abbeef5b1788f4a52b61eedd2f3dd07f81f2.zip | |
move all storage-related services into storage tree (#571)
* move all storage-related services into storage tree
* move config into config/
* also move admin service into storage since it mostly has storage related functionality
* header consolidation
Diffstat (limited to 'src/zenserver/workspaces/httpworkspaces.cpp')
| -rw-r--r-- | src/zenserver/workspaces/httpworkspaces.cpp | 1211 |
1 files changed, 0 insertions, 1211 deletions
diff --git a/src/zenserver/workspaces/httpworkspaces.cpp b/src/zenserver/workspaces/httpworkspaces.cpp deleted file mode 100644 index 7ef84743e..000000000 --- a/src/zenserver/workspaces/httpworkspaces.cpp +++ /dev/null @@ -1,1211 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include <workspaces/httpworkspaces.h> - -#include <zencore/basicfile.h> -#include <zencore/compactbinarybuilder.h> -#include <zencore/fmtutils.h> -#include <zencore/logging.h> -#include <zencore/trace.h> -#include <zenstore/workspaces.h> -#include <zenutil/chunkrequests.h> -#include <zenutil/workerpools.h> - -#include <unordered_set> - -namespace zen { -using namespace std::literals; - -ZEN_DEFINE_LOG_CATEGORY_STATIC(LogFs, "fs"sv); - -namespace { - - std::filesystem::path GetPathParameter(HttpServerRequest& ServerRequest, std::string_view Name) - { - if (std::string_view Value = ServerRequest.GetQueryParams().GetValue(Name); !Value.empty()) - { - return std::filesystem::path(HttpServerRequest::Decode(Value)); - } - return {}; - } - - void WriteWorkspaceConfig(CbWriter& Writer, const Workspaces::WorkspaceConfiguration& Config) - { - Writer << "id" << Config.Id; - Writer << "root_path" << Config.RootPath.string(); // utf8? - Writer << "allow_share_creation_from_http" << Config.AllowShareCreationFromHttp; - }; - - void WriteWorkspaceShareConfig(CbWriter& Writer, const Workspaces::WorkspaceShareConfiguration& Config) - { - Writer << "id" << Config.Id; - Writer << "share_path" << Config.SharePath.string(); // utf8? - if (!Config.Alias.empty()) - { - Writer << "alias" << Config.Alias; - } - }; - - void WriteWorkspaceAndSharesConfig(CbWriter& Writer, Workspaces& Workspaces, const Workspaces::WorkspaceConfiguration& WorkspaceConfig) - { - WriteWorkspaceConfig(Writer, WorkspaceConfig); - if (std::optional<std::vector<Oid>> ShareIds = Workspaces.GetWorkspaceShares(WorkspaceConfig.Id); ShareIds) - { - Writer.BeginArray("shares"); - { - for (const Oid& ShareId : *ShareIds) - { - if (std::optional<Workspaces::WorkspaceShareConfiguration> WorkspaceShareConfig = - Workspaces.GetWorkspaceShareConfiguration(WorkspaceConfig.Id, ShareId); - WorkspaceShareConfig) - { - Writer.BeginObject(); - { - WriteWorkspaceShareConfig(Writer, *WorkspaceShareConfig); - } - Writer.EndObject(); - } - } - } - Writer.EndArray(); - } - } - -} // namespace - -HttpWorkspacesService::HttpWorkspacesService(HttpStatusService& StatusService, - HttpStatsService& StatsService, - const WorkspacesServeConfig& Cfg, - Workspaces& Workspaces) -: m_Log(logging::Get("workspaces")) -, m_StatusService(StatusService) -, m_StatsService(StatsService) -, m_Config(Cfg) -, m_Workspaces(Workspaces) -{ - Initialize(); -} - -HttpWorkspacesService::~HttpWorkspacesService() -{ - m_StatsService.UnregisterHandler("ws", *this); - m_StatusService.UnregisterHandler("ws", *this); -} - -const char* -HttpWorkspacesService::BaseUri() const -{ - return "/ws/"; -} - -void -HttpWorkspacesService::HandleRequest(HttpServerRequest& Request) -{ - metrics::OperationTiming::Scope $(m_HttpRequests); - - if (m_Router.HandleRequest(Request) == false) - { - ZEN_LOG_WARN(LogFs, "No route found for {0}", Request.RelativeUri()); - return Request.WriteResponse(HttpResponseCode::NotFound, HttpContentType::kText, "Not found"sv); - } -} - -void -HttpWorkspacesService::HandleStatsRequest(HttpServerRequest& HttpReq) -{ - ZEN_TRACE_CPU("WorkspacesService::Stats"); - CbObjectWriter Cbo; - - EmitSnapshot("requests", m_HttpRequests, Cbo); - - Cbo.BeginObject("workspaces"); - { - Cbo.BeginObject("workspace"); - { - Cbo << "readcount" << m_WorkspacesStats.WorkspaceReadCount << "writecount" << m_WorkspacesStats.WorkspaceWriteCount - << "deletecount" << m_WorkspacesStats.WorkspaceDeleteCount; - } - Cbo.EndObject(); - - Cbo.BeginObject("workspaceshare"); - { - Cbo << "readcount" << m_WorkspacesStats.WorkspaceShareReadCount << "writecount" << m_WorkspacesStats.WorkspaceShareWriteCount - << "deletecount" << m_WorkspacesStats.WorkspaceShareDeleteCount; - } - Cbo.EndObject(); - - Cbo.BeginObject("chunk"); - { - Cbo << "hitcount" << m_WorkspacesStats.WorkspaceShareChunkHitCount << "misscount" - << m_WorkspacesStats.WorkspaceShareChunkMissCount; - } - Cbo.EndObject(); - - Cbo << "filescount" << m_WorkspacesStats.WorkspaceShareFilesReadCount; - Cbo << "entriescount" << m_WorkspacesStats.WorkspaceShareEntriesReadCount; - Cbo << "batchcount" << m_WorkspacesStats.WorkspaceShareBatchReadCount; - - Cbo << "requestcount" << m_WorkspacesStats.RequestCount; - Cbo << "badrequestcount" << m_WorkspacesStats.BadRequestCount; - } - Cbo.EndObject(); - - return HttpReq.WriteResponse(HttpResponseCode::OK, Cbo.Save()); -} - -void -HttpWorkspacesService::HandleStatusRequest(HttpServerRequest& Request) -{ - ZEN_TRACE_CPU("HttpWorkspacesService::Status"); - CbObjectWriter Cbo; - Cbo << "ok" << true; - Request.WriteResponse(HttpResponseCode::OK, Cbo.Save()); -} - -void -HttpWorkspacesService::Initialize() -{ - using namespace std::literals; - - ZEN_LOG_INFO(LogFs, "Initializing Workspaces Service"); - - m_Router.AddPattern("workspace_id", "([[:xdigit:]]{24})"); - m_Router.AddPattern("share_id", "([[:xdigit:]]{24})"); - m_Router.AddPattern("chunk", "([[:xdigit:]]{24})"); - m_Router.AddPattern("share_alias", "([[:alnum:]_.\\+\\-\\[\\]]+)"); - - m_Router.RegisterRoute( - "{workspace_id}/{share_id}/files", - [this](HttpRouterRequest& Req) { FilesRequest(Req); }, - HttpVerb::kGet); - - m_Router.RegisterRoute( - "{workspace_id}/{share_id}/{chunk}/info", - [this](HttpRouterRequest& Req) { ChunkInfoRequest(Req); }, - HttpVerb::kGet); - - m_Router.RegisterRoute( - "{workspace_id}/{share_id}/batch", - [this](HttpRouterRequest& Req) { BatchRequest(Req); }, - HttpVerb::kPost); - - m_Router.RegisterRoute( - "{workspace_id}/{share_id}/entries", - [this](HttpRouterRequest& Req) { EntriesRequest(Req); }, - HttpVerb::kGet); - - m_Router.RegisterRoute( - "{workspace_id}/{share_id}/{chunk}", - [this](HttpRouterRequest& Req) { ChunkRequest(Req); }, - HttpVerb::kGet | HttpVerb::kHead); - - m_Router.RegisterRoute( - "share/{share_alias}/files", - [this](HttpRouterRequest& Req) { ShareAliasFilesRequest(Req); }, - HttpVerb::kGet); - - m_Router.RegisterRoute( - "share/{share_alias}/{chunk}/info", - [this](HttpRouterRequest& Req) { ShareAliasChunkInfoRequest(Req); }, - HttpVerb::kGet); - - m_Router.RegisterRoute( - "share/{share_alias}/batch", - [this](HttpRouterRequest& Req) { ShareAliasBatchRequest(Req); }, - HttpVerb::kPost); - - m_Router.RegisterRoute( - "share/{share_alias}/entries", - [this](HttpRouterRequest& Req) { ShareAliasEntriesRequest(Req); }, - HttpVerb::kGet); - - m_Router.RegisterRoute( - "share/{share_alias}/{chunk}", - [this](HttpRouterRequest& Req) { ShareAliasChunkRequest(Req); }, - HttpVerb::kGet | HttpVerb::kHead); - - m_Router.RegisterRoute( - "share/{share_alias}", - [this](HttpRouterRequest& Req) { ShareAliasRequest(Req); }, - HttpVerb::kPut | HttpVerb::kGet | HttpVerb::kDelete); - - m_Router.RegisterRoute( - "{workspace_id}/{share_id}", - [this](HttpRouterRequest& Req) { ShareRequest(Req); }, - HttpVerb::kPut | HttpVerb::kGet | HttpVerb::kDelete); - - m_Router.RegisterRoute( - "{workspace_id}", - [this](HttpRouterRequest& Req) { WorkspaceRequest(Req); }, - HttpVerb::kPut | HttpVerb::kGet | HttpVerb::kDelete); - - m_Router.RegisterRoute( - "refresh", - [this](HttpRouterRequest& Req) { RefreshRequest(Req); }, - HttpVerb::kGet); - - m_Router.RegisterRoute( - "", - [this](HttpRouterRequest& Req) { WorkspacesRequest(Req); }, - HttpVerb::kGet); - - RefreshState(); - - m_StatsService.RegisterHandler("ws", *this); - m_StatusService.RegisterHandler("ws", *this); -} - -std::filesystem::path -HttpWorkspacesService::GetStatePath() const -{ - return m_Config.SystemRootDir / "workspaces"; -} - -void -HttpWorkspacesService::RefreshState() -{ - if (!m_Config.SystemRootDir.empty()) - { - m_Workspaces.RefreshState(GetStatePath()); - } -} - -bool -HttpWorkspacesService::MayChangeConfiguration(const HttpServerRequest& Req) const -{ - ZEN_UNUSED(Req); - return m_Config.AllowConfigurationChanges; -} - -void -HttpWorkspacesService::RefreshRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - RefreshState(); - return ServerRequest.WriteResponse(HttpResponseCode::OK); -} - -void -HttpWorkspacesService::WorkspacesRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - - std::vector<Oid> WorkspaceIds = m_Workspaces.GetWorkspaces(); - CbObjectWriter Response; - Response.BeginArray("workspaces"); - for (const Oid& WorkspaceId : WorkspaceIds) - { - if (std::optional<Workspaces::WorkspaceConfiguration> WorkspaceConfig = m_Workspaces.GetWorkspaceConfiguration(WorkspaceId); - WorkspaceConfig) - { - Response.BeginObject(); - { - WriteWorkspaceAndSharesConfig(Response, m_Workspaces, *WorkspaceConfig); - } - Response.EndObject(); - } - } - Response.EndArray(); - - return ServerRequest.WriteResponse(HttpResponseCode::OK, Response.Save()); -} - -void -HttpWorkspacesService::FilesRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - const Oid WorkspaceId = Oid::TryFromHexString(Req.GetCapture(1)); - if (WorkspaceId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid workspace id '{}'", Req.GetCapture(1))); - } - const Oid ShareId = Oid::TryFromHexString(Req.GetCapture(2)); - if (ShareId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid share id '{}'", Req.GetCapture(2))); - } - FilesRequest(Req, WorkspaceId, ShareId); -} - -void -HttpWorkspacesService::ChunkInfoRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - const Oid WorkspaceId = Oid::TryFromHexString(Req.GetCapture(1)); - if (WorkspaceId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid workspace id '{}'", Req.GetCapture(1))); - } - const Oid ShareId = Oid::TryFromHexString(Req.GetCapture(2)); - if (ShareId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid share id '{}'", Req.GetCapture(2))); - } - const Oid ChunkId = Oid::TryFromHexString(Req.GetCapture(3)); - if (ChunkId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid chunk id '{}'", Req.GetCapture(3))); - } - ChunkInfoRequest(Req, WorkspaceId, ShareId, ChunkId); -} - -void -HttpWorkspacesService::BatchRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - const Oid WorkspaceId = Oid::TryFromHexString(Req.GetCapture(1)); - if (WorkspaceId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid workspace id '{}'", Req.GetCapture(1))); - } - const Oid ShareId = Oid::TryFromHexString(Req.GetCapture(2)); - if (ShareId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid share id '{}'", Req.GetCapture(2))); - } - BatchRequest(Req, WorkspaceId, ShareId); -} - -void -HttpWorkspacesService::EntriesRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - const Oid WorkspaceId = Oid::TryFromHexString(Req.GetCapture(1)); - if (WorkspaceId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid workspace id '{}'", Req.GetCapture(1))); - } - const Oid ShareId = Oid::TryFromHexString(Req.GetCapture(2)); - if (ShareId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid share id '{}'", Req.GetCapture(2))); - } - EntriesRequest(Req, WorkspaceId, ShareId); -} - -void -HttpWorkspacesService::ChunkRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - const Oid WorkspaceId = Oid::TryFromHexString(Req.GetCapture(1)); - if (WorkspaceId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid workspace id '{}'", Req.GetCapture(1))); - } - const Oid ShareId = Oid::TryFromHexString(Req.GetCapture(2)); - if (ShareId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid share id '{}'", Req.GetCapture(2))); - } - const Oid ChunkId = Oid::TryFromHexString(Req.GetCapture(3)); - if (ChunkId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid chunk id '{}'", Req.GetCapture(3))); - } - ChunkRequest(Req, WorkspaceId, ShareId, ChunkId); -} - -void -HttpWorkspacesService::ShareRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - const Oid WorkspaceId = Oid::TryFromHexString(Req.GetCapture(1)); - if (WorkspaceId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid workspace id '{}'", Req.GetCapture(1))); - } - Oid ShareId = Oid::Zero; - if (Req.GetCapture(2) != Oid::Zero.ToString()) - { - ShareId = Oid::TryFromHexString(Req.GetCapture(2)); - if (ShareId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid share id '{}'", Req.GetCapture(2))); - } - } - ShareRequest(Req, WorkspaceId, ShareId); -} - -void -HttpWorkspacesService::WorkspaceRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - Oid WorkspaceId = Oid::TryFromHexString(Req.GetCapture(1)); - switch (ServerRequest.RequestVerb()) - { - case HttpVerb::kPut: - { - std::filesystem::path WorkspacePath = GetPathParameter(ServerRequest, "root_path"sv); - if (WorkspacePath.empty()) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - "Invalid 'root_path' parameter"); - } - - if (Req.GetCapture(1) == Oid::Zero.ToString()) - { - // Synthesize Id - WorkspaceId = Workspaces::PathToId(WorkspacePath); - ZEN_INFO("Generated workspace id from path '{}': {}", WorkspacePath, WorkspaceId); - } - else if (WorkspaceId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid workspace id '{}'", Req.GetCapture(1))); - } - - if (!MayChangeConfiguration(ServerRequest)) - { - return ServerRequest.WriteResponse(HttpResponseCode::Unauthorized, - HttpContentType::kText, - fmt::format("Adding workspace {} is not allowed", WorkspaceId)); - } - bool AllowShareCreationFromHttp = false; - if (std::string_view Value = ServerRequest.GetQueryParams().GetValue("allow_share_creation_from_http"); Value == "true"sv) - { - AllowShareCreationFromHttp = true; - } - - m_WorkspacesStats.WorkspaceWriteCount++; - Workspaces::WorkspaceConfiguration OldConfig = Workspaces::FindWorkspace(Log(), GetStatePath(), WorkspaceId); - Workspaces::WorkspaceConfiguration NewConfig = {.Id = WorkspaceId, - .RootPath = WorkspacePath, - .AllowShareCreationFromHttp = AllowShareCreationFromHttp}; - if (OldConfig.Id == WorkspaceId && (OldConfig != NewConfig)) - { - return ServerRequest.WriteResponse( - HttpResponseCode::Conflict, - HttpContentType::kText, - fmt::format("Workspace {} already exists with root path '{}'", WorkspaceId, OldConfig.RootPath)); - } - else if (OldConfig.Id == Oid::Zero) - { - if (Workspaces::WorkspaceConfiguration ConfigWithSameRoot = - Workspaces::FindWorkspace(Log(), GetStatePath(), WorkspacePath); - ConfigWithSameRoot.Id != Oid::Zero) - { - return ServerRequest.WriteResponse( - HttpResponseCode::Conflict, - HttpContentType::kText, - fmt::format("Workspace {} already exists with same root path '{}'", ConfigWithSameRoot.Id, WorkspacePath)); - } - } - - bool Created = Workspaces::AddWorkspace(Log(), GetStatePath(), NewConfig); - if (Created) - { - ZEN_ASSERT(OldConfig.Id == Oid::Zero); - RefreshState(); - return ServerRequest.WriteResponse(HttpResponseCode::Created, HttpContentType::kText, fmt::format("{}", WorkspaceId)); - } - else - { - return ServerRequest.WriteResponse(HttpResponseCode::OK, HttpContentType::kText, fmt::format("{}", WorkspaceId)); - } - } - case HttpVerb::kGet: - { - if (WorkspaceId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid workspace id '{}'", Req.GetCapture(1))); - } - m_WorkspacesStats.WorkspaceReadCount++; - std::optional<Workspaces::WorkspaceConfiguration> Workspace = m_Workspaces.GetWorkspaceConfiguration(WorkspaceId); - if (Workspace) - { - CbObjectWriter Response; - WriteWorkspaceAndSharesConfig(Response, m_Workspaces, *Workspace); - return ServerRequest.WriteResponse(HttpResponseCode::OK, Response.Save()); - } - else - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - } - case HttpVerb::kDelete: - { - if (WorkspaceId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid workspace id '{}'", Req.GetCapture(1))); - } - - if (!MayChangeConfiguration(ServerRequest)) - { - return ServerRequest.WriteResponse(HttpResponseCode::Unauthorized, - HttpContentType::kText, - fmt::format("Removing workspace {} is not allowed", WorkspaceId)); - } - - m_WorkspacesStats.WorkspaceDeleteCount++; - bool Deleted = Workspaces::RemoveWorkspace(Log(), GetStatePath(), WorkspaceId); - if (Deleted) - { - RefreshState(); - return ServerRequest.WriteResponse(HttpResponseCode::OK); - } - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - } -} - -void -HttpWorkspacesService::ShareAliasFilesRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - std::string_view Alias = Req.GetCapture(1); - if (Alias.empty()) - { - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid alias '{}'", Req.GetCapture(1))); - } - std::optional<Workspaces::ShareAlias> WorkspaceAndShareId = m_Workspaces.GetShareAlias(Alias); - if (!WorkspaceAndShareId.has_value()) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - FilesRequest(Req, WorkspaceAndShareId.value().WorkspaceId, WorkspaceAndShareId.value().ShareId); -} - -void -HttpWorkspacesService::ShareAliasChunkInfoRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - std::string_view Alias = Req.GetCapture(1); - if (Alias.empty()) - { - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid alias '{}'", Req.GetCapture(1))); - } - std::optional<Workspaces::ShareAlias> WorkspaceAndShareId = m_Workspaces.GetShareAlias(Alias); - if (!WorkspaceAndShareId.has_value()) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - const Oid ChunkId = Oid::TryFromHexString(Req.GetCapture(2)); - if (ChunkId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid chunk id '{}'", Req.GetCapture(2))); - } - ChunkInfoRequest(Req, WorkspaceAndShareId.value().WorkspaceId, WorkspaceAndShareId.value().ShareId, ChunkId); -} - -void -HttpWorkspacesService::ShareAliasBatchRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - std::string_view Alias = Req.GetCapture(1); - if (Alias.empty()) - { - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid alias '{}'", Req.GetCapture(1))); - } - std::optional<Workspaces::ShareAlias> WorkspaceAndShareId = m_Workspaces.GetShareAlias(Alias); - if (!WorkspaceAndShareId.has_value()) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - BatchRequest(Req, WorkspaceAndShareId.value().WorkspaceId, WorkspaceAndShareId.value().ShareId); -} - -void -HttpWorkspacesService::ShareAliasEntriesRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - std::string_view Alias = Req.GetCapture(1); - if (Alias.empty()) - { - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid alias '{}'", Req.GetCapture(1))); - } - std::optional<Workspaces::ShareAlias> WorkspaceAndShareId = m_Workspaces.GetShareAlias(Alias); - if (!WorkspaceAndShareId.has_value()) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - EntriesRequest(Req, WorkspaceAndShareId.value().WorkspaceId, WorkspaceAndShareId.value().ShareId); -} - -void -HttpWorkspacesService::ShareAliasChunkRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - std::string_view Alias = Req.GetCapture(1); - if (Alias.empty()) - { - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid alias '{}'", Req.GetCapture(1))); - } - std::optional<Workspaces::ShareAlias> WorkspaceAndShareId = m_Workspaces.GetShareAlias(Alias); - if (!WorkspaceAndShareId.has_value()) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - const Oid ChunkId = Oid::TryFromHexString(Req.GetCapture(2)); - if (ChunkId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid chunk id '{}'", Req.GetCapture(2))); - } - ChunkRequest(Req, WorkspaceAndShareId.value().WorkspaceId, WorkspaceAndShareId.value().ShareId, ChunkId); -} - -void -HttpWorkspacesService::ShareAliasRequest(HttpRouterRequest& Req) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - std::string_view Alias = Req.GetCapture(1); - if (Alias.empty()) - { - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid alias '{}'", Req.GetCapture(1))); - } - std::optional<Workspaces::ShareAlias> WorkspaceAndShareId = m_Workspaces.GetShareAlias(Alias); - if (!WorkspaceAndShareId.has_value()) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - ShareRequest(Req, WorkspaceAndShareId.value().WorkspaceId, WorkspaceAndShareId.value().ShareId); -} - -void -HttpWorkspacesService::FilesRequest(HttpRouterRequest& Req, const Oid& WorkspaceId, const Oid& ShareId) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - - m_WorkspacesStats.WorkspaceShareFilesReadCount++; - - std::unordered_set<std::string> WantedFieldNames; - if (auto FieldFilter = HttpServerRequest::Decode(ServerRequest.GetQueryParams().GetValue("fieldnames")); !FieldFilter.empty()) - { - if (FieldFilter != "*") // Get all - empty FieldFilter equal getting all fields - { - ForEachStrTok(FieldFilter, ',', [&](std::string_view FieldName) { - WantedFieldNames.insert(std::string(FieldName)); - return true; - }); - } - } - else - { - const bool FilterClient = ServerRequest.GetQueryParams().GetValue("filter"sv) == "client"sv; - WantedFieldNames.insert("id"); - WantedFieldNames.insert("clientpath"); - if (!FilterClient) - { - WantedFieldNames.insert("serverpath"); - } - } - - bool Refresh = false; - if (auto RefreshStr = ServerRequest.GetQueryParams().GetValue("refresh"); !RefreshStr.empty()) - { - Refresh = StrCaseCompare(std::string(RefreshStr).c_str(), "true") == 0; - } - - const bool WantsAllFields = WantedFieldNames.empty(); - - const bool WantsIdField = WantsAllFields || WantedFieldNames.contains("id"); - const bool WantsClientPathField = WantsAllFields || WantedFieldNames.contains("clientpath"); - const bool WantsServerPathField = WantsAllFields || WantedFieldNames.contains("serverpath"); - const bool WantsRawSizeField = WantsAllFields || WantedFieldNames.contains("rawsize"); - const bool WantsSizeField = WantsAllFields || WantedFieldNames.contains("size"); - - std::optional<std::vector<Workspaces::ShareFile>> Files = - m_Workspaces.GetWorkspaceShareFiles(WorkspaceId, ShareId, Refresh, GetSmallWorkerPool(EWorkloadType::Burst)); - if (!Files.has_value()) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - - CbObjectWriter Response; - Response.BeginArray("files"sv); - { - for (const Workspaces::ShareFile& Entry : Files.value()) - { - Response.BeginObject(); - if (WantsIdField) - { - Response << "id"sv << Entry.Id; - } - if (WantsServerPathField) - { - Response << "serverpath"sv << Entry.RelativePath; - } - if (WantsClientPathField) - { - Response << "clientpath"sv << Entry.RelativePath; - } - if (WantsSizeField) - { - Response << "size"sv << Entry.Size; - } - if (WantsRawSizeField) - { - Response << "rawsize"sv << Entry.Size; - } - Response.EndObject(); - } - } - Response.EndArray(); - - return ServerRequest.WriteResponse(HttpResponseCode::OK, Response.Save()); -} - -void -HttpWorkspacesService::ChunkInfoRequest(HttpRouterRequest& Req, const Oid& WorkspaceId, const Oid& ShareId, const Oid& ChunkId) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - Workspaces::ShareFile File = - m_Workspaces.GetWorkspaceShareChunkInfo(WorkspaceId, ShareId, ChunkId, GetSmallWorkerPool(EWorkloadType::Burst)); - if (File.Id != Oid::Zero) - { - CbObjectWriter Response; - Response << "size"sv << File.Size; - m_WorkspacesStats.WorkspaceShareChunkHitCount++; - return ServerRequest.WriteResponse(HttpResponseCode::OK, Response.Save()); - } - m_WorkspacesStats.WorkspaceShareChunkMissCount++; - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); -} - -void -HttpWorkspacesService::BatchRequest(HttpRouterRequest& Req, const Oid& WorkspaceId, const Oid& ShareId) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - IoBuffer Payload = ServerRequest.ReadPayload(); - std::optional<std::vector<RequestChunkEntry>> ChunkRequests = ParseChunkBatchRequest(Payload); - if (!ChunkRequests.has_value()) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, HttpContentType::kText, "batch payload malformed"); - } - m_WorkspacesStats.WorkspaceShareBatchReadCount++; - std::vector<Workspaces::ChunkRequest> Requests; - Requests.reserve(ChunkRequests.value().size()); - std::transform(ChunkRequests.value().begin(), - ChunkRequests.value().end(), - std::back_inserter(Requests), - [](const RequestChunkEntry& Entry) { - return Workspaces::ChunkRequest{.ChunkId = Entry.ChunkId, .Offset = Entry.Offset, .Size = Entry.RequestBytes}; - }); - std::vector<IoBuffer> Chunks = - m_Workspaces.GetWorkspaceShareChunks(WorkspaceId, ShareId, Requests, GetSmallWorkerPool(EWorkloadType::Burst)); - if (Chunks.empty()) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - for (const IoBuffer& Buffer : Chunks) - { - if (Buffer) - { - m_WorkspacesStats.WorkspaceShareChunkHitCount++; - } - else - { - m_WorkspacesStats.WorkspaceShareChunkMissCount++; - } - } - std::vector<IoBuffer> Response = BuildChunkBatchResponse(ChunkRequests.value(), Chunks); - if (!Response.empty()) - { - return ServerRequest.WriteResponse(HttpResponseCode::OK, HttpContentType::kBinary, Response); - } - return ServerRequest.WriteResponse(HttpResponseCode::InternalServerError, - HttpContentType::kText, - fmt::format("failed formatting response for batch of {} chunks", Chunks.size())); -} - -void -HttpWorkspacesService::EntriesRequest(HttpRouterRequest& Req, const Oid& WorkspaceId, const Oid& ShareId) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - std::string_view OpKey = ServerRequest.GetQueryParams().GetValue("opkey"sv); - if (!OpKey.empty() && OpKey != "file_manifest") - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - std::unordered_set<std::string> WantedFieldNames; - if (auto FieldFilter = HttpServerRequest::Decode(ServerRequest.GetQueryParams().GetValue("fieldfilter")); !FieldFilter.empty()) - { - if (FieldFilter != "*") // Get all - empty FieldFilter equal getting all fields - { - ForEachStrTok(FieldFilter, ',', [&](std::string_view FieldName) { - WantedFieldNames.insert(std::string(FieldName)); - return true; - }); - } - } - - bool Refresh = false; - if (auto RefreshStr = ServerRequest.GetQueryParams().GetValue("refresh"); !RefreshStr.empty()) - { - Refresh = StrCaseCompare(std::string(RefreshStr).c_str(), "true") == 0; - } - - m_WorkspacesStats.WorkspaceShareEntriesReadCount++; - std::optional<std::vector<Workspaces::ShareFile>> Files = - m_Workspaces.GetWorkspaceShareFiles(WorkspaceId, ShareId, Refresh, GetSmallWorkerPool(EWorkloadType::Burst)); - if (!Files.has_value()) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - const bool WantsAllFields = WantedFieldNames.empty(); - - const bool WantsIdField = WantsAllFields || WantedFieldNames.contains("id"); - const bool WantsClientPathField = WantsAllFields || WantedFieldNames.contains("clientpath"); - const bool WantsServerPathField = WantsAllFields || WantedFieldNames.contains("serverpath"); - - CbObjectWriter Response; - - if (OpKey.empty()) - { - Response.BeginArray("entries"sv); - Response.BeginObject(); - } - else - { - Response.BeginObject("entry"sv); - } - { - // Synthesize a fake op - Response << "key" - << "file_manifest"; - - Response.BeginArray("files"); - { - for (const Workspaces::ShareFile& Entry : Files.value()) - { - Response.BeginObject(); - { - if (WantsIdField) - { - Response << "id"sv << Entry.Id; - } - if (WantsServerPathField) - { - Response << "serverpath"sv << Entry.RelativePath; - } - if (WantsClientPathField) - { - Response << "clientpath"sv << Entry.RelativePath; - } - } - Response.EndObject(); - } - } - Response.EndArray(); - } - - if (OpKey.empty()) - { - Response.EndObject(); - Response.EndArray(); - } - else - { - Response.EndObject(); - } - - return ServerRequest.WriteResponse(HttpResponseCode::OK, Response.Save()); -} - -void -HttpWorkspacesService::ChunkRequest(HttpRouterRequest& Req, const Oid& WorkspaceId, const Oid& ShareId, const Oid& ChunkId) -{ - HttpServerRequest& ServerRequest = Req.ServerRequest(); - - uint64_t Offset = 0; - uint64_t Size = ~(0ull); - if (auto OffsetParm = ServerRequest.GetQueryParams().GetValue("offset"); OffsetParm.empty() == false) - { - if (auto OffsetVal = ParseInt<uint64_t>(OffsetParm)) - { - Offset = OffsetVal.value(); - } - else - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid offset parameter '{}'", OffsetParm)); - } - } - - if (auto SizeParm = ServerRequest.GetQueryParams().GetValue("size"); SizeParm.empty() == false) - { - if (auto SizeVal = ParseInt<uint64_t>(SizeParm)) - { - Size = SizeVal.value(); - } - else - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid size parameter '{}'", SizeParm)); - } - } - - std::vector<IoBuffer> Response = m_Workspaces.GetWorkspaceShareChunks( - WorkspaceId, - ShareId, - std::vector<Workspaces::ChunkRequest>{Workspaces::ChunkRequest{.ChunkId = ChunkId, .Offset = Offset, .Size = Size}}, - GetSmallWorkerPool(EWorkloadType::Burst)); - if (!Response.empty() && Response[0]) - { - m_WorkspacesStats.WorkspaceShareChunkHitCount++; - if (Response[0].GetSize() == 0) - { - return ServerRequest.WriteResponse(HttpResponseCode::OK); - } - return ServerRequest.WriteResponse(HttpResponseCode::OK, Response[0].GetContentType(), Response); - } - m_WorkspacesStats.WorkspaceShareChunkMissCount++; - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); -} - -void -HttpWorkspacesService::ShareRequest(HttpRouterRequest& Req, const Oid& WorkspaceId, const Oid& InShareId) -{ - Oid ShareId = InShareId; - - HttpServerRequest& ServerRequest = Req.ServerRequest(); - switch (ServerRequest.RequestVerb()) - { - case HttpVerb::kPut: - { - std::filesystem::path SharePath = GetPathParameter(ServerRequest, "share_path"sv); - if (SharePath.empty()) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - "Invalid 'share_path' parameter"); - } - - if (ShareId == Oid::Zero) - { - // Synthesize Id - ShareId = Workspaces::PathToId(SharePath); - ZEN_INFO("Generated workspace id from path '{}': {}", SharePath, ShareId); - } - - std::string Alias = HttpServerRequest::Decode(ServerRequest.GetQueryParams().GetValue("alias"sv)); - if (!AsciiSet::HasOnly(Alias, Workspaces::ValidAliasCharactersSet)) - { - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, HttpContentType::kText, "Invalid 'alias' parameter"); - } - - Workspaces::WorkspaceConfiguration Workspace = Workspaces::FindWorkspace(Log(), GetStatePath(), WorkspaceId); - if (Workspace.Id == Oid::Zero) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound, - HttpContentType::kText, - fmt::format("Workspace '{}' does not exist", WorkspaceId)); - } - - if (!Workspace.AllowShareCreationFromHttp) - { - if (!MayChangeConfiguration(ServerRequest)) - { - return ServerRequest.WriteResponse( - HttpResponseCode::Unauthorized, - HttpContentType::kText, - fmt::format("Adding workspace share {} in workspace {} is not allowed", WorkspaceId, ShareId)); - } - } - - m_WorkspacesStats.WorkspaceShareWriteCount++; - - const Workspaces::WorkspaceShareConfiguration OldConfig = - Workspaces::FindWorkspaceShare(Log(), Workspace.RootPath, ShareId); - const Workspaces::WorkspaceShareConfiguration NewConfig = {.Id = ShareId, - .SharePath = SharePath, - .Alias = std::string(Alias)}; - - if (OldConfig.Id == ShareId && (OldConfig != NewConfig)) - { - return ServerRequest.WriteResponse( - HttpResponseCode::Conflict, - HttpContentType::kText, - fmt::format("Workspace share '{}' already exist in workspace '{}' with share path '{}' and alias '{}'", - ShareId, - WorkspaceId, - OldConfig.SharePath, - OldConfig.Alias)); - } - else if (OldConfig.Id == Oid::Zero) - { - if (Workspaces::WorkspaceShareConfiguration ConfigWithSamePath = - Workspaces::FindWorkspaceShare(Log(), Workspace.RootPath, SharePath); - ConfigWithSamePath.Id != Oid::Zero) - { - return ServerRequest.WriteResponse( - HttpResponseCode::Conflict, - HttpContentType::kText, - fmt::format("Workspace share '{}' already exist in workspace '{}' with same share path '{}' and alias '{}'", - ShareId, - WorkspaceId, - OldConfig.SharePath, - OldConfig.Alias)); - } - } - - if (!IsDir(Workspace.RootPath / NewConfig.SharePath)) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound, - HttpContentType::kText, - fmt::format("directory {} does not exist in workspace {} root '{}'", - NewConfig.SharePath, - WorkspaceId, - Workspace.RootPath)); - } - - bool Created = Workspaces::AddWorkspaceShare(Log(), Workspace.RootPath, NewConfig); - if (Created) - { - RefreshState(); - return ServerRequest.WriteResponse(HttpResponseCode::Created, HttpContentType::kText, fmt::format("{}", ShareId)); - } - return ServerRequest.WriteResponse(HttpResponseCode::OK, HttpContentType::kText, fmt::format("{}", ShareId)); - } - case HttpVerb::kGet: - { - if (WorkspaceId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid workspace id '{}'", Req.GetCapture(1))); - } - if (ShareId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid share id '{}'", ShareId)); - } - - m_WorkspacesStats.WorkspaceShareReadCount++; - std::optional<Workspaces::WorkspaceShareConfiguration> Config = - m_Workspaces.GetWorkspaceShareConfiguration(WorkspaceId, ShareId); - if (!Config) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - - CbObjectWriter Response; - WriteWorkspaceShareConfig(Response, *Config); - return ServerRequest.WriteResponse(HttpResponseCode::OK, Response.Save()); - } - case HttpVerb::kDelete: - { - if (WorkspaceId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid workspace id '{}'", Req.GetCapture(1))); - } - if (ShareId == Oid::Zero) - { - m_WorkspacesStats.BadRequestCount++; - return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, - HttpContentType::kText, - fmt::format("Invalid share id '{}'", ShareId)); - } - - Workspaces::WorkspaceConfiguration Workspace = Workspaces::FindWorkspace(Log(), GetStatePath(), WorkspaceId); - if (Workspace.Id == Oid::Zero) - { - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - - if (!Workspace.AllowShareCreationFromHttp) - { - if (!MayChangeConfiguration(ServerRequest)) - { - return ServerRequest.WriteResponse( - HttpResponseCode::Unauthorized, - HttpContentType::kText, - fmt::format("Removing workspace share {} in workspace {} is not allowed", WorkspaceId, ShareId)); - } - } - - m_WorkspacesStats.WorkspaceShareDeleteCount++; - bool Deleted = Workspaces::RemoveWorkspaceShare(Log(), Workspace.RootPath, ShareId); - if (Deleted) - { - RefreshState(); - return ServerRequest.WriteResponse(HttpResponseCode::OK); - } - return ServerRequest.WriteResponse(HttpResponseCode::NotFound); - } - } -} - -} // namespace zen |