// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Containers/Array.h" #include "Containers/Map.h" #include "Containers/UnrealString.h" #include "CoreTypes.h" #include "HttpPath.h" #include "HttpRequestHandler.h" #include "HttpResultCallback.h" #include "HttpRouteHandle.h" #include "HttpServerRequest.h" #include "IHttpRouter.h" #include "Logging/LogMacros.h" #include "UObject/NameTypes.h" #include "UObject/Object.h" #include "UObject/ObjectMacros.h" #include "UObject/UObjectGlobals.h" #include "ExternalRpcRegistry.generated.h" DECLARE_LOG_CATEGORY_EXTERN(LogExternalRpcRegistry, Log, All); USTRUCT() struct FExternalRpcArgumentDesc { GENERATED_BODY() public: FString Name; FString Type; FString Desc; bool bIsOptional = false; FExternalRpcArgumentDesc() = default; FExternalRpcArgumentDesc(FString InName, FString InType, FString InDesc, bool bInIsOptional = false) { Name = InName; Type = InType; Desc = InDesc; bIsOptional = bInIsOptional; } bool operator==(const FExternalRpcArgumentDesc& Other) const { return Name == Other.Name && Type == Other.Type && Desc == Other.Desc && bIsOptional == Other.bIsOptional; } bool operator!=(const FExternalRpcArgumentDesc& Other) const { return !(*this == Other); } }; USTRUCT() struct FExternalRouteInfo { GENERATED_BODY() public: FName RouteName; FHttpPath RoutePath; EHttpServerRequestVerbs RequestVerbs = EHttpServerRequestVerbs::VERB_NONE; FString InputContentType; TArray ExpectedArguments; FString RpcCategory = TEXT("Unknown"); bool bAlwaysOn = false; FExternalRouteInfo() = default; FExternalRouteInfo(FName InRouteName, FHttpPath InRoutePath, EHttpServerRequestVerbs InRequestVerbs, FString InCategory = TEXT("Unknown"), bool bInAlwaysOn = false, FString InContentType = {}, TArray InArguments = TArray()) { RouteName = InRouteName; RoutePath = InRoutePath; RequestVerbs = InRequestVerbs; InputContentType = InContentType; ExpectedArguments = InArguments; RpcCategory = InCategory; bAlwaysOn = false; } bool operator==(const FExternalRouteInfo& Other) const { return RouteName == Other.RouteName && RoutePath == Other.RoutePath && RequestVerbs == Other.RequestVerbs && InputContentType == Other.InputContentType && ExpectedArguments == Other.ExpectedArguments && RpcCategory == Other.RpcCategory && bAlwaysOn == Other.bAlwaysOn; } bool operator!=(const FExternalRouteInfo& Other) const { return !(*this == Other); } }; USTRUCT() struct FExternalRouteDesc { GENERATED_BODY() public: FHttpRouteHandle Handle; FString InputContentType; FString RpcCategory; TArray ExpectedArguments; FExternalRouteDesc() = default; FExternalRouteDesc(FHttpRouteHandle InHandle, FString InContentType, TArray InArguments, FString InCategory = "") { Handle = InHandle; InputContentType = InContentType; ExpectedArguments = InArguments; RpcCategory = InCategory; } }; USTRUCT() struct FRpcLedgerEntry { GENERATED_BODY() public: FString RpcName; FString RequestBody; FDateTime RequestTime; FRpcLedgerEntry() = default; FRpcLedgerEntry(FString InName, FString InBody) { RpcName = InName; RequestBody = InBody; RequestTime = FDateTime::UtcNow(); } }; /** * This class is designed to be a singleton that handles registry, maintenance, and cleanup of any REST endpoints exposed on the process * for use in communicating with the process externally. */ UCLASS(MinimalAPI) class UExternalRpcRegistry : public UObject { GENERATED_BODY() protected: static EXTERNALRPCREGISTRY_API UExternalRpcRegistry * ObjectInstance; TMap RegisteredRoutes; TArray ActiveRpcCategories; TArray RequestLedger; public: static EXTERNALRPCREGISTRY_API UExternalRpcRegistry * GetInstance(); static EXTERNALRPCREGISTRY_API bool IsEnabled(); EXTERNALRPCREGISTRY_API ~UExternalRpcRegistry(); int PortToUse = 11223; int RequestLedgerCapacity = 10; /** * Check if this Rpc is from a category that is meant to be enabled. */ EXTERNALRPCREGISTRY_API bool IsActiveRpcCategory(FString InCategory); /** * Try to get a route registered under given friendly name. Returns false if could not be found. */ EXTERNALRPCREGISTRY_API bool GetRegisteredRoute(FName RouteName, FExternalRouteInfo& OutRouteInfo); EXTERNALRPCREGISTRY_API void RegisterNewRoute(FExternalRouteInfo InRouteInfo, const FHttpRequestHandler& Handler, bool bOverrideIfBound = false); /** * Register a new route. * Will override existing routes if option is set, otherwise will error and fail to bind. */ EXTERNALRPCREGISTRY_API void RegisterNewRouteWithArguments(FName RouteName, const FHttpPath& HttpPath, const EHttpServerRequestVerbs& RequestVerbs, const FHttpRequestHandler& Handler, TArray InArguments, bool bOverrideIfBound = false, bool bIsAlwaysOn = false, FString OptionalCategory = TEXT("Unknown"), FString OptionalContentType = {}); /** * Deprecated way to register a new route. * Will override existing routes if option is set, otherwise will error and fail to bind. */ EXTERNALRPCREGISTRY_API void RegisterNewRoute(FName RouteName, const FHttpPath& HttpPath, const EHttpServerRequestVerbs& RequestVerbs, const FHttpRequestHandler& Handler, bool bOverrideIfBound = false, bool bIsAlwaysOn = false, FString OptionalCategory = TEXT("Unknown"), FString OptionalContentType = {}, FString OptionalExpectedFormat = {}); /** * Clean up all routes - generally called as part of the destructor to make sure we don't have any function pointers dangling around. */ EXTERNALRPCREGISTRY_API void CleanUpAllRoutes(); /** * Clean up a route. * Can be set to fail if trying to unbind an unbound route. */ EXTERNALRPCREGISTRY_API void CleanUpRoute(FName RouteName, bool bFailIfUnbound = false); /** * Default Route Listing http call. Spits out all registered routes and describes them via a REST API call. * Always registered at /listrpcs GET by default */ EXTERNALRPCREGISTRY_API bool HttpListOpenRoutes(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete); /** * Records an RPC call into the ledger, plus trims the ledger if it is too large. */ EXTERNALRPCREGISTRY_API void AddRequestToLedger(const FHttpServerRequest& Request); /** * Default request history call. Lists all calls recorded in the ledger, showing RPC name, request time, request body * Always registered at /requesthistory GET by default */ EXTERNALRPCREGISTRY_API bool HttpPrintRequestLedger(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete); /** * Route Listing http, formatted for Swagger OASv3. Spits out all registered routes and describes them via a REST API call. * Always registered at /swagger.json GET by default */ EXTERNALRPCREGISTRY_API bool HttpListOASv3JSONRoutes(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete); /** * Route Showing SwaggerUI using /swagger.json to describe the current REST API. * Always registered at /swagger/html.index GET by default */ EXTERNALRPCREGISTRY_API bool HttpSwaggerUI(const FHttpServerRequest& Request, const FHttpResultCallback& OnComplete); };