Files
UnrealEngineUWP/Engine/Source/Runtime/Online/HTTP/Private/HttpManager.cpp
louisphilippe seguin d5aa4c2bc7 Undo changelist 12957826 since its causing crashes in CurlRequests in non-multithread servers
[REVIEW]

#ROBOMERGE-SOURCE: CL 12974227 via CL 12974230 via CL 12974232 via CL 12974234 via CL 12974236
#ROBOMERGE-BOT: RELEASE (Release-Engine-Staging -> Main) (v682-12900288)

[CL 12974237 by louisphilippe seguin in Main branch]
2020-04-22 09:30:24 -04:00

280 lines
6.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "HttpManager.h"
#include "HttpModule.h"
#include "HAL/PlatformTime.h"
#include "HAL/PlatformProcess.h"
#include "Misc/ScopeLock.h"
#include "Http.h"
#include "Misc/Guid.h"
#include "Misc/Fork.h"
#include "HttpThread.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/CommandLine.h"
#include "Stats/Stats.h"
#include "Containers/BackgroundableTicker.h"
// FHttpManager
FCriticalSection FHttpManager::RequestLock;
FHttpManager::FHttpManager()
: FTickerObjectBase(0.0f, FBackgroundableTicker::GetCoreTicker())
, Thread(nullptr)
, CorrelationIdMethod(FHttpManager::GetDefaultCorrelationIdMethod())
{
}
FHttpManager::~FHttpManager()
{
if (Thread)
{
Thread->StopThread();
delete Thread;
}
}
void FHttpManager::Initialize()
{
if (FPlatformHttp::UsesThreadedHttp())
{
Thread = CreateHttpThread();
Thread->StartThread();
}
}
void FHttpManager::SetCorrelationIdMethod(TFunction<FString()> InCorrelationIdMethod)
{
check(InCorrelationIdMethod);
CorrelationIdMethod = MoveTemp(InCorrelationIdMethod);
}
FString FHttpManager::CreateCorrelationId() const
{
return CorrelationIdMethod();
}
bool FHttpManager::IsDomainAllowed(const FString& Url) const
{
#if !UE_BUILD_SHIPPING
#if !(UE_GAME || UE_SERVER)
// Whitelist is opt-in in non-shipping non-game/server builds
static const bool bEnableWhitelist = FParse::Param(FCommandLine::Get(), TEXT("EnableHttpWhitelist"));
if (!bEnableWhitelist)
{
return true;
}
#else
// Allow non-shipping game/server builds to disable the whitelist check
static const bool bDisableWhitelist = FParse::Param(FCommandLine::Get(), TEXT("DisableHttpWhitelist"));
if (bDisableWhitelist)
{
return true;
}
#endif
#endif // !UE_BUILD_SHIPPING
// check to see if the Domain is white-listed (or no white-list specified)
const TArray<FString>& AllowedDomains = FHttpModule::Get().GetAllowedDomains();
if (AllowedDomains.Num() > 0)
{
const FString Domain = FPlatformHttp::GetUrlDomain(Url);
for (const FString& AllowedDomain : AllowedDomains)
{
if (Domain.EndsWith(AllowedDomain))
{
return true;
}
}
return false;
}
return true;
}
/*static*/
TFunction<FString()> FHttpManager::GetDefaultCorrelationIdMethod()
{
return []{ return FGuid::NewGuid().ToString(); };
}
void FHttpManager::OnBeforeFork()
{
Flush(false);
}
void FHttpManager::OnAfterFork()
{
}
void FHttpManager::UpdateConfigs()
{
// empty
}
FHttpThread* FHttpManager::CreateHttpThread()
{
return new FHttpThread();
}
void FHttpManager::Flush(bool bShutdown)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FHttpManager_Flush);
FScopeLock ScopeLock(&RequestLock);
double MaxFlushTimeSeconds = -1.0; // default to no limit
GConfig->GetDouble(TEXT("HTTP"), TEXT("MaxFlushTimeSeconds"), MaxFlushTimeSeconds, GEngineIni);
if (bShutdown)
{
if (Requests.Num())
{
UE_LOG(LogHttp, Display, TEXT("Http module shutting down, but needs to wait on %d outstanding Http requests:"), Requests.Num());
}
// Clear delegates since they may point to deleted instances
for (TArray<TSharedRef<IHttpRequest>>::TIterator It(Requests); It; ++It)
{
TSharedRef<IHttpRequest>& Request = *It;
Request->OnProcessRequestComplete().Unbind();
Request->OnRequestProgress().Unbind();
Request->OnHeaderReceived().Unbind();
UE_LOG(LogHttp, Display, TEXT(" verb=[%s] url=[%s] refs=[%d] status=%s"), *Request->GetVerb(), *Request->GetURL(), Request.GetSharedReferenceCount(), EHttpRequestStatus::ToString(Request->GetStatus()));
}
}
// block until all active requests have completed
double BeginWaitTime = FPlatformTime::Seconds();
double LastTime = BeginWaitTime;
while (Requests.Num() > 0)
{
const double AppTime = FPlatformTime::Seconds();
//UE_LOG(LogHttp, Display, TEXT("Waiting for %0.2f seconds. Limit:%0.2f seconds"), (AppTime - BeginWaitTime), MaxFlushTimeSeconds);
if (bShutdown && MaxFlushTimeSeconds > 0 && (AppTime - BeginWaitTime > MaxFlushTimeSeconds))
{
UE_LOG(LogHttp, Display, TEXT("Canceling remaining HTTP requests after waiting %0.2f seconds"), (AppTime - BeginWaitTime));
for (TArray<TSharedRef<IHttpRequest>>::TIterator It(Requests); It; ++It)
{
TSharedRef<IHttpRequest>& Request = *It;
if (IsEngineExitRequested())
{
ensureMsgf(Request.IsUnique(), TEXT("Dangling HTTP request! This may cause undefined behaviour or crash during module shutdown!"));
}
Request->CancelRequest();
}
}
Tick(AppTime - LastTime);
LastTime = AppTime;
if (Requests.Num() > 0)
{
if (FPlatformProcess::SupportsMultithreading())
{
UE_LOG(LogHttp, Display, TEXT("Sleeping 0.5s to wait for %d outstanding Http requests."), Requests.Num());
FPlatformProcess::Sleep(0.5f);
}
else if (Thread)
{
Thread->Tick();
}
else
{
check(!FPlatformHttp::UsesThreadedHttp());
}
}
}
}
bool FHttpManager::Tick(float DeltaSeconds)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_FHttpManager_Tick);
FScopeLock ScopeLock(&RequestLock);
// Tick each active request
for (TArray<TSharedRef<IHttpRequest>>::TIterator It(Requests); It; ++It)
{
TSharedRef<IHttpRequest> Request = *It;
Request->Tick(DeltaSeconds);
}
if (Thread)
{
TArray<IHttpThreadedRequest*> CompletedThreadedRequests;
Thread->GetCompletedRequests(CompletedThreadedRequests);
// Finish and remove any completed requests
for (IHttpThreadedRequest* CompletedRequest : CompletedThreadedRequests)
{
CompletedRequest->FinishRequest();
Requests.Remove(CompletedRequest->AsShared());
}
}
// keep ticking
return true;
}
void FHttpManager::AddRequest(const TSharedRef<IHttpRequest>& Request)
{
FScopeLock ScopeLock(&RequestLock);
Requests.Add(Request);
}
void FHttpManager::RemoveRequest(const TSharedRef<IHttpRequest>& Request)
{
FScopeLock ScopeLock(&RequestLock);
Requests.Remove(Request);
}
void FHttpManager::AddThreadedRequest(const TSharedRef<IHttpThreadedRequest>& Request)
{
check(Thread);
{
FScopeLock ScopeLock(&RequestLock);
Requests.Add(Request);
}
Thread->AddRequest(&Request.Get());
}
void FHttpManager::CancelThreadedRequest(const TSharedRef<IHttpThreadedRequest>& Request)
{
check(Thread);
Thread->CancelRequest(&Request.Get());
}
bool FHttpManager::IsValidRequest(const IHttpRequest* RequestPtr) const
{
FScopeLock ScopeLock(&RequestLock);
bool bResult = false;
for (const TSharedRef<IHttpRequest>& Request : Requests)
{
if (&Request.Get() == RequestPtr)
{
bResult = true;
break;
}
}
return bResult;
}
void FHttpManager::DumpRequests(FOutputDevice& Ar) const
{
FScopeLock ScopeLock(&RequestLock);
Ar.Logf(TEXT("------- (%d) Http Requests"), Requests.Num());
for (const TSharedRef<IHttpRequest>& Request : Requests)
{
Ar.Logf(TEXT(" verb=[%s] url=[%s] status=%s"),
*Request->GetVerb(), *Request->GetURL(), EHttpRequestStatus::ToString(Request->GetStatus()));
}
}
bool FHttpManager::SupportsDynamicProxy() const
{
return false;
}