diff options
| author | Per Larsson <[email protected]> | 2022-01-24 11:11:10 +0100 |
|---|---|---|
| committer | Per Larsson <[email protected]> | 2022-01-24 11:11:10 +0100 |
| commit | dc6becffb513280170958f94e18c1b2966ade4d1 (patch) | |
| tree | c7f9cccafcc21e241abdecde6f5219ab1009aff6 /zenserver/upstream/upstreamservice.cpp | |
| parent | Format fix. (diff) | |
| download | zen-dc6becffb513280170958f94e18c1b2966ade4d1.tar.xz zen-dc6becffb513280170958f94e18c1b2966ade4d1.zip | |
Refactored upstream cache to better handle different states in prep for dynamic auth tokens.
Diffstat (limited to 'zenserver/upstream/upstreamservice.cpp')
| -rw-r--r-- | zenserver/upstream/upstreamservice.cpp | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/zenserver/upstream/upstreamservice.cpp b/zenserver/upstream/upstreamservice.cpp new file mode 100644 index 000000000..1cfd1df85 --- /dev/null +++ b/zenserver/upstream/upstreamservice.cpp @@ -0,0 +1,208 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include <upstream/jupiter.h> +#include <upstream/upstreamcache.h> +#include <upstream/upstreamservice.h> +#include <upstream/zen.h> +#include <zencore/compactbinarybuilder.h> +#include <zencore/string.h> + +#include <json11.hpp> + +namespace zen { + +using namespace std::literals; + +namespace { + json11::Json TryGetJson(IoBuffer Body, HttpContentType ContentType, std::string& OutError) + { + if (!Body) + { + OutError = "No data"sv; + return json11::Json(); + } + + if ((ContentType == HttpContentType::kJSON || ContentType == HttpContentType::kCbObject) == false) + { + OutError = "Invalid content type"sv; + return json11::Json(); + } + + if (ContentType == ZenContentType::kJSON) + { + std::string JsonText(reinterpret_cast<const char*>(Body.GetData()), Body.GetSize()); + return json11::Json::parse(JsonText, OutError); + } + + if (CbObject Obj = LoadCompactBinaryObject(Body)) + { + ExtendableStringBuilder<512> Sb; + return json11::Json::parse(Obj.ToJson(Sb).ToString(), OutError); + } + + OutError = "Invalid compact binary"sv; + return json11::Json(); + } + + void WriteErrorResponse(HttpServerRequest& Request, std::string_view Property, std::string_view Reason) + { + CbObjectWriter Response; + Response << "Result"sv << false; + Response.BeginObject("Error"sv); + Response << "Property"sv << Property << "Reason"sv << Reason; + Response.EndObject(); + + Request.WriteResponse(HttpResponseCode::BadRequest, Response.Save()); + } + + void WriteSuccessResponse(HttpServerRequest& Request) + { + CbObjectWriter Response; + Response << "Result"sv << true; + + Request.WriteResponse(HttpResponseCode::OK, Response.Save()); + } +} // namespace + +HttpUpstreamService::HttpUpstreamService(UpstreamCache& Upstream) : m_Upstream(Upstream) +{ + m_Router.RegisterRoute( + "endpoints", + [this](HttpRouterRequest& Req) { + CbObjectWriter Writer; + Writer.BeginArray("Endpoints"sv); + m_Upstream.IterateEndpoints([&Writer](UpstreamEndpoint& Ep) { + UpstreamEndpointInfo Info = Ep.GetEndpointInfo(); + UpstreamEndpointStatus Status = Ep.GetStatus(); + + Writer.BeginObject(); + Writer << "Name"sv << Info.Name; + Writer << "Url"sv << Info.Url; + Writer << "State"sv << ToString(Status.State); + Writer << "Reason"sv << Status.Reason; + Writer.EndObject(); + + return true; + }); + Writer.EndArray(); + Req.ServerRequest().WriteResponse(HttpResponseCode::OK, Writer.Save()); + }, + HttpVerb::kGet); + + m_Router.RegisterRoute( + "endpoints", + [this](HttpRouterRequest& RouterRequest) { + HttpServerRequest& ServerRequest = RouterRequest.ServerRequest(); + std::string JsonError; + + json11::Json Json = TryGetJson(ServerRequest.ReadPayload(), ServerRequest.RequestContentType(), JsonError); + + if (!JsonError.empty()) + { + return ServerRequest.WriteResponse(HttpResponseCode::BadRequest, HttpContentType::kText, JsonError); + } + + const auto Type = Json["Type"].string_value(); + const auto Name = Json["Name"].string_value(); + const auto Url = Json["Url"].string_value(); + const auto Namespace = Json["Namespace"].string_value(); + const auto OAuthProvider = Json["OAuthProvider"].string_value(); + const auto OAuthClientId = Json["OAuthClientId"].string_value(); + const auto OAuthSecret = Json["OAuthSecret"].string_value(); + const auto OAuthToken = Json["OAuthToken"].string_value(); + + if ((Type == "Horde"sv || Type == "Zen"sv) == false) + { + return WriteErrorResponse(ServerRequest, "Type"sv, "Invalid endpoint type, must be Zen or Horde"sv); + } + + if (Name.empty()) + { + return WriteErrorResponse(ServerRequest, "Name"sv, "Invalid endpoint name"sv); + } + + if (Url.empty()) + { + return WriteErrorResponse(ServerRequest, "Url"sv, "Invalid endpoint URL"sv); + } + + bool IsNameUnique = true; + m_Upstream.IterateEndpoints([&Name, &IsNameUnique](UpstreamEndpoint& Ep) { + IsNameUnique = IsNameUnique && Ep.GetEndpointInfo().Name != Name; + return IsNameUnique; + }); + + if (IsNameUnique == false) + { + return WriteErrorResponse(ServerRequest, "Url"sv, "Endpoint name is not unique"sv); + } + + std::unique_ptr<zen::UpstreamEndpoint> Endpoint; + + if (Type == "Zen"sv) + { + std::vector<std::string> Urls; + Urls.push_back(Json["Url"].string_value()); + Endpoint = zen::MakeZenUpstreamEndpoint({.Name = Name, .Urls = Urls}); + } + else + { + if (Namespace.empty()) + { + return WriteErrorResponse(ServerRequest, "Namespace"sv, "Invalid Horde namespace"sv); + } + + if (OAuthProvider.empty()) + { + return WriteErrorResponse(ServerRequest, "OAuthProvider"sv, "Invalid Horde OAuth provider URL"sv); + } + + if (OAuthToken.empty()) + { + if (OAuthClientId.empty()) + { + return WriteErrorResponse(ServerRequest, "OAuthClientId"sv, "Invalid Horde OAuth client ID"sv); + } + + if (OAuthSecret.empty()) + { + return WriteErrorResponse(ServerRequest, "OAuthSecret"sv, "Invalid Horde OAuth secret"sv); + } + } + + const zen::CloudCacheClientOptions Options = {.Name = Name, + .ServiceUrl = Url, + .DdcNamespace = Namespace, + .BlobStoreNamespace = Namespace, + .OAuthProvider = OAuthProvider, + .OAuthClientId = OAuthClientId, + .OAuthSecret = OAuthSecret, + .AccessToken = OAuthToken}; + + Endpoint = zen::MakeJupiterUpstreamEndpoint(Options); + } + + m_Upstream.RegisterEndpoint(std::move(Endpoint)); + + WriteSuccessResponse(ServerRequest); + }, + HttpVerb::kPost); +} + +HttpUpstreamService::~HttpUpstreamService() +{ +} + +const char* +HttpUpstreamService::BaseUri() const +{ + return "/upstream/"; +} + +void +HttpUpstreamService::HandleRequest(zen::HttpServerRequest& Request) +{ + m_Router.HandleRequest(Request); +} + +} // namespace zen |