Files
UnrealEngine/Engine/Source/Runtime/Online/HTTPServer/Private/HttpServerModule.cpp
2025-05-18 13:04:45 +08:00

196 lines
4.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "HttpServerModule.h"
#include "HttpListener.h"
#include "Modules/ModuleManager.h"
#include "Stats/Stats.h"
class FHttpServerModuleImpl
{
public:
/** The association of port bindings and respective HTTP listeners */
TMap<uint32, TUniquePtr<FHttpListener>> Listeners;
/** Whether listeners can be started */
bool bHttpListenersEnabled = false;
};
DEFINE_LOG_CATEGORY(LogHttpServerModule);
// FHttpServerModule
IMPLEMENT_MODULE(FHttpServerModule, HTTPServer);
FHttpServerModule* FHttpServerModule::Singleton = nullptr;
FHttpServerModule::FHttpServerModule()
: Impl(new FHttpServerModuleImpl)
{
}
FHttpServerModule::~FHttpServerModule()
{
delete Impl;
Impl = nullptr;
}
void FHttpServerModule::StartupModule()
{
Singleton = this;
}
void FHttpServerModule::ShutdownModule()
{
// stop all listeners
StopAllListeners();
// destroy all listeners
Impl->Listeners.Empty();
Singleton = nullptr;
}
void FHttpServerModule::StartAllListeners()
{
Impl->bHttpListenersEnabled = true;
UE_LOG(LogHttpServerModule, Log,
TEXT("Starting all listeners..."));
for (const auto& Listener : Impl->Listeners)
{
if (!Listener.Value->IsListening())
{
Listener.Value->StartListening();
}
}
UE_LOG(LogHttpServerModule, Log,
TEXT("All listeners started"));
}
void FHttpServerModule::StopAllListeners()
{
UE_LOG(LogHttpServerModule, Log,
TEXT("Stopping all listeners..."));
Impl->bHttpListenersEnabled = false;
for (const auto& Listener : Impl->Listeners)
{
if (Listener.Value->IsListening())
{
Listener.Value->StopListening();
}
}
UE_LOG(LogHttpServerModule, Log,
TEXT("All listeners stopped"));
}
bool FHttpServerModule::HasPendingListeners() const
{
for (const auto& Listener : Impl->Listeners)
{
if (Listener.Value->HasPendingConnections())
{
return true;
}
}
return false;
}
bool FHttpServerModule::IsAvailable()
{
return nullptr != Singleton;
}
FHttpServerModule& FHttpServerModule::Get()
{
if (nullptr == Singleton)
{
check(IsInGameThread());
FModuleManager::LoadModuleChecked<FHttpServerModule>("HTTPServer");
}
check(Singleton);
return *Singleton;
}
TSharedPtr<IHttpRouter> FHttpServerModule::GetHttpRouter(uint32 Port, bool bFailOnBindFailure /* = false */)
{
check(Singleton == this);
bool bFailedToListen = false;
// We may already have a listener for this port
TUniquePtr<FHttpListener>* ExistingListener = Impl->Listeners.Find(Port);
if (ExistingListener)
{
if (Impl->bHttpListenersEnabled)
{
// if listeners are enabled, the existing listener for this port
// should always be listening (IsListening() will only be true
// if it fully initialized/successfully bound and actually started listening)
if (ExistingListener->Get()->IsListening())
{
UE_LOG(LogHttpServerModule, Verbose, TEXT("[%s] found an existing, active listener for port %d"), ANSI_TO_TCHAR(__FUNCTION__), Port);
return ExistingListener->Get()->GetRouter();
}
else
{
// get rid of it and create a new one for now
UE_LOG(LogHttpServerModule, Error, TEXT("[%s] the existing listener for port %d is not listening/bound and listeners are still enabled"),
ANSI_TO_TCHAR(__FUNCTION__), Port);
Impl->Listeners.Remove(Port);
}
}
else
{
UE_LOG(LogHttpServerModule, Verbose, TEXT("[%s] found an existing listener for port %d but listeners are currently disabled"), ANSI_TO_TCHAR(__FUNCTION__), Port);
return ExistingListener->Get()->GetRouter();
}
}
// Otherwise create a new one
UE_LOG(LogHttpServerModule, VeryVerbose, TEXT("[%s] creating a new listener for port %d"), ANSI_TO_TCHAR(__FUNCTION__), Port);
TUniquePtr<FHttpListener> NewListener = MakeUnique<FHttpListener>(Port);
// Try to start this listener now
if (Impl->bHttpListenersEnabled)
{
if (!NewListener->StartListening())
{
UE_LOG(LogHttpServerModule, Warning, TEXT("[%s] failed to start listening on port %d! (bFailOnBindFailure? %s)"), ANSI_TO_TCHAR(__FUNCTION__), Port, *LexToString(bFailOnBindFailure));
bFailedToListen = true;
}
}
if (bFailedToListen && bFailOnBindFailure)
{
// if we actually failed to listen on a port for whatever reason
// (i.e. listeners are enabled and we attempted to do so) then
// let the one we created fall out of scope and try again next time
return nullptr;
}
else
{
// the legacy behavior returns the router regardless of listener success
const auto& NewListenerRef = Impl->Listeners.Add(Port, MoveTemp(NewListener));
return NewListenerRef->GetRouter();
}
}
bool FHttpServerModule::Tick(float DeltaTime)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FHttpServerModule_Tick);
check(Singleton == this);
if (Impl->bHttpListenersEnabled)
{
for (const auto& Listener : Impl->Listeners)
{
Listener.Value->Tick(DeltaTime);
}
}
return true;
}