From e00be420081452cf371b9855e11794013d07c666 Mon Sep 17 00:00:00 2001 From: Mark Tamarov Date: Mon, 12 Aug 2024 14:54:39 +0300 Subject: [PATCH 1/4] feat: added processing of get requests --- .../Private/PsWebServerHandlerImpl.cpp | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/Source/PsWebServer/Private/PsWebServerHandlerImpl.cpp b/Source/PsWebServer/Private/PsWebServerHandlerImpl.cpp index ca7fad0..979b929 100644 --- a/Source/PsWebServer/Private/PsWebServerHandlerImpl.cpp +++ b/Source/PsWebServer/Private/PsWebServerHandlerImpl.cpp @@ -29,13 +29,44 @@ FPsWebServerHandlerImpl::FPsWebServerHandlerImpl() CachedResponseHeaders = PrintHeadersToString(ResponseHeaders); } +bool FPsWebServerHandlerImpl::handleGet(CivetServer* Server, mg_connection* RequestConnection) +{ + const FGuid RequestId = FGuid::NewGuid(); + + const auto Handler = OwnerHandler.Load(); + if (!Handler || Handler == nullptr) + { + const FString StatusCode = TEXT("503 Service Unavailable"); + const FString ResponseData = TEXT("Request handler is not valid"); + return SendResponse(RequestConnection, RequestId, StatusCode, ResponseData); + } + + // Create cancellation source + auto CancellationSource = FPsWebCancellationSource{}; + const auto CancellationToken = CancellationSource.GetToken(); + + FEvent* const RequestReadyEvent = CreateContext(RequestConnection, RequestId, MoveTemp(CancellationSource)); + FString PostData = PsWebServerUtils::GetPostData(RequestConnection); + + FString UeBuf(mg_get_request_info(RequestConnection)->query_string); + + // Set request processing on the game thread + DECLARE_DELEGATE(FTaskDelegate); + auto TaskDelegate = FTaskDelegate::CreateWeakLambda(Handler, [Handler, RequestId, PostData = MoveTemp(UeBuf), CancellationToken]() mutable { + Handler->SetRequestOnNextTick(RequestId, MoveTemp(PostData), CancellationToken); + }); + AsyncTask(ENamedThreads::GameThread, [TaskDelegate = MoveTemp(TaskDelegate)] { TaskDelegate.Execute(); }); + + return WaitForResponse(RequestConnection, RequestReadyEvent, RequestId); +} + bool FPsWebServerHandlerImpl::handlePost(CivetServer* Server, mg_connection* RequestConnection) { // Unique request id const FGuid RequestId = FGuid::NewGuid(); const auto Handler = OwnerHandler.Load(); - if (!Handler || Handler->IsPendingKill()) + if (!Handler || Handler == nullptr) { const FString StatusCode = TEXT("503 Service Unavailable"); const FString ResponseData = TEXT("Request handler is not valid"); @@ -166,7 +197,7 @@ FString FPsWebServerHandlerImpl::GetResponseData(const FGuid& RequestId, bool bT Context.CancellationSource.Cancel(); const auto Handler = OwnerHandler.Load(); - if (Handler && !Handler->IsPendingKill()) + if (Handler && Handler == nullptr) { return Handler->GetTimeoutResponse(); } From b53ed7f2bef6d8f7b0db40eb8fc05d84417565cb Mon Sep 17 00:00:00 2001 From: Mark Tamarov Date: Mon, 12 Aug 2024 14:57:07 +0300 Subject: [PATCH 2/4] feat: added processing of get requests --- Source/PsWebServer/Public/PsWebServerHandlerImpl.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/PsWebServer/Public/PsWebServerHandlerImpl.h b/Source/PsWebServer/Public/PsWebServerHandlerImpl.h index 2bb901e..3f3b0d7 100644 --- a/Source/PsWebServer/Public/PsWebServerHandlerImpl.h +++ b/Source/PsWebServer/Public/PsWebServerHandlerImpl.h @@ -27,6 +27,9 @@ class FPsWebServerHandlerImpl : public CivetHandler, public TSharedFromThis Date: Mon, 12 Aug 2024 15:21:33 +0300 Subject: [PATCH 3/4] Update README.md --- README.md | 82 ++++++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 436d399..dbd6df2 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,63 @@ # PsWebServer -Civet web server integration plugin for Unreal Engine 4 +Civet web server integration plugin for Unreal Engine 5 -# HowTo +## HowTo Web server usage (f.e. in AMyGameMode::BeginPlay())) - ```cpp -// Launch web server -WebServer = NewObject(this); -WebServer->StartServer(); - -// Create and register api handler -auto PingHandler = NewObject(this); -PingHandler->SetHeader(TEXT("Server"), TEXT("MyServer/") + MyGI->GetGameVersion()); // Optional header set -WebServer->AddHandler(PingHandler, TEXT("/api/ping")); +void AMyGameMode::BeginPlay() +{ + Super::BeginPlay(); + + auto PlayerController = Cast(UGameplayStatics::GetPlayerController(this, 0)); + + WebServer = NewObject(this); + WebServer->StartServer(); + + const auto SomeOAuthHandler = NewObject(this); + SomeOAuthHandler->PlayerController = PlayerController; + SomeOAuthHandler->SetHeader( + TEXT("Content-Type"), + TEXT("text/plain; charset=utf-8") + ); + WebServer->AddHandler(SomeOAuthHandler, TEXT("/some_oauth/")); + + PlayerController->OnAuthCompleteEvent.BindLambda([this](FString Response) + { + WebServer->StopServer(); + }); +} ``` -If uses `UMyServerPingHandler` class that defined as: - +USomeOAuthHandler.h ```cpp -#pragma once - -#include "PsWebServerHandler.h" - -#include "MyServerPingHandler.generated.h" - UCLASS() -class UMyServerPingHandler : public UPsWebServerHandler +class USomeOAuthHandler : public UPsWebServerHandler { GENERATED_BODY() public: - /** Override to implement your custom logic of request processing */ virtual void ProcessRequest_Implementation(const FGuid& RequestUniqueId, const FString& RequestData) override; -}; +public: + UPROPERTY() + APlayerController* PlayerController = nullptr; +}; ``` -And its definition: - +USomeOAuthHandler.cpp ```cpp -#include "MyServerPingHandler.h" - -#include "PsWebServerDefines.h" -#include "VaRestJsonObject.h" - -/* - * Check that request has valid json encoded body and return its copy in response - */ -void UMyServerPingHandler::ProcessRequest_Implementation(const FGuid& RequestUniqueId, const FString& RequestData) +void USomeOAuthHandler::ProcessRequest_Implementation(const FGuid& RequestUniqueId, const FString& RequestData) { - // Validate json format with VaRest - UVaRestJsonObject* JsonTemp = UVaRestJsonObject::ConstructJsonObject(this); - if (JsonTemp->DecodeJson(RequestData)) + if (!RequestData.IsEmpty() && PlayerController) { - ProcessRequestFinish(RequestUniqueId, FString::Printf(TEXT("{\"request_data\":%s}"), *RequestData)); - return; + PlayerController->OnAuthCompleteEvent.Execute(RequestData); } - - UE_LOG(LogMyGame, Error, TEXT("%s: can't validate data as json one: %s"), *PS_FUNC_LINE, *RequestData); - - const FString ErrorStr = TEXT(R"({"error":"1000","message":"Request data is not a valid json object"})"); - ProcessRequestFinish(RequestUniqueId, ErrorStr); + + ProcessRequestFinish(RequestUniqueId, FString::Printf(TEXT("{\"request_data\":%s}"), *RequestData)); + return; } ``` + +The user can now access the resource through this link: ```http://127.0.0.1:2050/some_oauth/``` From 9d7d5c744bce73f0cd5d7e2c3f817f153a48ccba Mon Sep 17 00:00:00 2001 From: Mark Tamarov Date: Mon, 12 Aug 2024 15:28:18 +0300 Subject: [PATCH 4/4] Revert "Update README.md" This reverts commit 383d9b96e78d59a3065b07f49fc6a531a7381fcb. --- README.md | 82 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index dbd6df2..436d399 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,67 @@ # PsWebServer -Civet web server integration plugin for Unreal Engine 5 +Civet web server integration plugin for Unreal Engine 4 -## HowTo +# HowTo Web server usage (f.e. in AMyGameMode::BeginPlay())) + ```cpp -void AMyGameMode::BeginPlay() -{ - Super::BeginPlay(); - - auto PlayerController = Cast(UGameplayStatics::GetPlayerController(this, 0)); - - WebServer = NewObject(this); - WebServer->StartServer(); - - const auto SomeOAuthHandler = NewObject(this); - SomeOAuthHandler->PlayerController = PlayerController; - SomeOAuthHandler->SetHeader( - TEXT("Content-Type"), - TEXT("text/plain; charset=utf-8") - ); - WebServer->AddHandler(SomeOAuthHandler, TEXT("/some_oauth/")); - - PlayerController->OnAuthCompleteEvent.BindLambda([this](FString Response) - { - WebServer->StopServer(); - }); -} +// Launch web server +WebServer = NewObject(this); +WebServer->StartServer(); + +// Create and register api handler +auto PingHandler = NewObject(this); +PingHandler->SetHeader(TEXT("Server"), TEXT("MyServer/") + MyGI->GetGameVersion()); // Optional header set +WebServer->AddHandler(PingHandler, TEXT("/api/ping")); ``` -USomeOAuthHandler.h +If uses `UMyServerPingHandler` class that defined as: + ```cpp +#pragma once + +#include "PsWebServerHandler.h" + +#include "MyServerPingHandler.generated.h" + UCLASS() -class USomeOAuthHandler : public UPsWebServerHandler +class UMyServerPingHandler : public UPsWebServerHandler { GENERATED_BODY() public: + /** Override to implement your custom logic of request processing */ virtual void ProcessRequest_Implementation(const FGuid& RequestUniqueId, const FString& RequestData) override; - -public: - UPROPERTY() - APlayerController* PlayerController = nullptr; }; + ``` -USomeOAuthHandler.cpp +And its definition: + ```cpp -void USomeOAuthHandler::ProcessRequest_Implementation(const FGuid& RequestUniqueId, const FString& RequestData) +#include "MyServerPingHandler.h" + +#include "PsWebServerDefines.h" +#include "VaRestJsonObject.h" + +/* + * Check that request has valid json encoded body and return its copy in response + */ +void UMyServerPingHandler::ProcessRequest_Implementation(const FGuid& RequestUniqueId, const FString& RequestData) { - if (!RequestData.IsEmpty() && PlayerController) + // Validate json format with VaRest + UVaRestJsonObject* JsonTemp = UVaRestJsonObject::ConstructJsonObject(this); + if (JsonTemp->DecodeJson(RequestData)) { - PlayerController->OnAuthCompleteEvent.Execute(RequestData); + ProcessRequestFinish(RequestUniqueId, FString::Printf(TEXT("{\"request_data\":%s}"), *RequestData)); + return; } - - ProcessRequestFinish(RequestUniqueId, FString::Printf(TEXT("{\"request_data\":%s}"), *RequestData)); - return; + + UE_LOG(LogMyGame, Error, TEXT("%s: can't validate data as json one: %s"), *PS_FUNC_LINE, *RequestData); + + const FString ErrorStr = TEXT(R"({"error":"1000","message":"Request data is not a valid json object"})"); + ProcessRequestFinish(RequestUniqueId, ErrorStr); } ``` - -The user can now access the resource through this link: ```http://127.0.0.1:2050/some_oauth/```