2023-06-02 20:28:07 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "GenericPlatform/HttpRequestCommon.h"
2023-11-16 21:27:48 -05:00
# include "GenericPlatform/HttpResponseCommon.h"
2024-01-26 14:25:18 -05:00
# include "HAL/Event.h"
2023-06-02 20:28:07 -04:00
# include "Http.h"
# include "HttpManager.h"
2024-01-23 16:08:08 -05:00
# include "Misc/CommandLine.h"
# include "Stats/Stats.h"
FHttpRequestCommon : : FHttpRequestCommon ( )
: RequestStartTimeAbsoluteSeconds ( FPlatformTime : : Seconds ( ) )
, ActivityTimeoutAt ( 0.0 )
{
}
2023-06-02 20:28:07 -04:00
2023-10-04 15:14:01 -04:00
FString FHttpRequestCommon : : GetURLParameter ( const FString & ParameterName ) const
{
FString ReturnValue ;
if ( TOptional < FString > OptionalParameterValue = FGenericPlatformHttp : : GetUrlParameter ( GetURL ( ) , ParameterName ) )
{
ReturnValue = MoveTemp ( OptionalParameterValue . GetValue ( ) ) ;
}
return ReturnValue ;
}
2023-06-02 20:28:07 -04:00
EHttpRequestStatus : : Type FHttpRequestCommon : : GetStatus ( ) const
{
return CompletionStatus ;
}
2024-02-05 13:23:49 -05:00
const FString & FHttpRequestCommon : : GetEffectiveURL ( ) const
{
return EffectiveURL ;
}
2023-12-05 17:39:04 -05:00
EHttpFailureReason FHttpRequestCommon : : GetFailureReason ( ) const
{
return FailureReason ;
}
2023-06-02 20:28:07 -04:00
bool FHttpRequestCommon : : PreCheck ( ) const
{
2023-12-18 10:32:06 -05:00
// Disabled http request processing
if ( ! FHttpModule : : Get ( ) . IsHttpEnabled ( ) )
{
UE_LOG ( LogHttp , Verbose , TEXT ( " Http disabled. Skipping request. url=%s " ) , * GetURL ( ) ) ;
return false ;
}
2023-06-02 20:28:07 -04:00
// Prevent overlapped requests using the same instance
if ( CompletionStatus = = EHttpRequestStatus : : Processing )
{
UE_LOG ( LogHttp , Warning , TEXT ( " ProcessRequest failed. Still processing last request. " ) ) ;
return false ;
}
// Nothing to do without a valid URL
if ( GetURL ( ) . IsEmpty ( ) )
{
UE_LOG ( LogHttp , Warning , TEXT ( " ProcessRequest failed. No URL was specified. " ) ) ;
return false ;
}
2023-10-04 15:14:01 -04:00
if ( GetVerb ( ) . IsEmpty ( ) )
{
UE_LOG ( LogHttp , Warning , TEXT ( " ProcessRequest failed. No Verb was specified. " ) ) ;
return false ;
}
2023-06-02 20:28:07 -04:00
if ( ! FHttpModule : : Get ( ) . GetHttpManager ( ) . IsDomainAllowed ( GetURL ( ) ) )
{
UE_LOG ( LogHttp , Warning , TEXT ( " ProcessRequest failed. URL '%s' is not using an allowed domain. " ) , * GetURL ( ) ) ;
return false ;
}
2024-01-23 16:08:08 -05:00
if ( bTimedOut )
{
UE_LOG ( LogHttp , Warning , TEXT ( " ProcessRequest failed. Request with URL '%s' already timed out. " ) , * GetURL ( ) ) ;
return false ;
}
2023-06-02 20:28:07 -04:00
return true ;
}
2023-12-07 13:38:15 -05:00
bool FHttpRequestCommon : : PreProcess ( )
{
ClearInCaseOfRetry ( ) ;
if ( ! PreCheck ( ) | | ! SetupRequest ( ) )
{
FinishRequestNotInHttpManager ( ) ;
return false ;
}
2024-01-23 16:08:08 -05:00
StartTotalTimeoutTimer ( ) ;
2023-12-18 10:32:06 -05:00
UE_LOG ( LogHttp , Verbose , TEXT ( " %p: Verb='%s' URL='%s' " ) , this , * GetVerb ( ) , * GetURL ( ) ) ;
2023-12-07 13:38:15 -05:00
return true ;
}
2024-01-23 16:08:08 -05:00
void FHttpRequestCommon : : PostProcess ( )
{
CleanupRequest ( ) ;
}
2023-12-07 13:38:15 -05:00
void FHttpRequestCommon : : ClearInCaseOfRetry ( )
{
2024-01-23 16:08:08 -05:00
bActivityTimedOut = false ;
2023-12-07 13:38:15 -05:00
FailureReason = EHttpFailureReason : : None ;
2024-01-23 16:08:08 -05:00
bCanceled = false ;
2024-02-05 13:23:49 -05:00
EffectiveURL = GetURL ( ) ;
2024-02-15 09:54:57 -05:00
ResponseCommon . Reset ( ) ;
2023-12-07 13:38:15 -05:00
}
void FHttpRequestCommon : : FinishRequestNotInHttpManager ( )
{
if ( IsInGameThread ( ) )
{
if ( DelegateThreadPolicy = = EHttpRequestDelegateThreadPolicy : : CompleteOnGameThread )
{
FinishRequest ( ) ;
}
else
{
FHttpModule : : Get ( ) . GetHttpManager ( ) . AddHttpThreadTask ( [ StrongThis = StaticCastSharedRef < FHttpRequestCommon > ( AsShared ( ) ) ] ( )
{
StrongThis - > FinishRequest ( ) ;
} ) ;
}
}
else
{
if ( DelegateThreadPolicy = = EHttpRequestDelegateThreadPolicy : : CompleteOnHttpThread )
{
FinishRequest ( ) ;
}
else
{
FHttpModule : : Get ( ) . GetHttpManager ( ) . AddGameThreadTask ( [ StrongThis = StaticCastSharedRef < FHttpRequestCommon > ( AsShared ( ) ) ] ( )
{
StrongThis - > FinishRequest ( ) ;
} ) ;
}
}
}
2023-06-08 14:54:12 -04:00
void FHttpRequestCommon : : SetDelegateThreadPolicy ( EHttpRequestDelegateThreadPolicy InDelegateThreadPolicy )
{
DelegateThreadPolicy = InDelegateThreadPolicy ;
}
EHttpRequestDelegateThreadPolicy FHttpRequestCommon : : GetDelegateThreadPolicy ( ) const
{
return DelegateThreadPolicy ;
}
2024-02-15 09:54:57 -05:00
void FHttpRequestCommon : : HandleRequestSucceed ( TSharedPtr < IHttpResponse > InResponse )
2024-01-10 14:19:37 -05:00
{
SetStatus ( EHttpRequestStatus : : Succeeded ) ;
2024-02-15 09:54:57 -05:00
OnProcessRequestComplete ( ) . ExecuteIfBound ( SharedThis ( this ) , InResponse , true ) ;
2024-01-10 14:19:37 -05:00
FHttpModule : : Get ( ) . GetHttpManager ( ) . RecordStatTimeToConnect ( ConnectTime ) ;
}
2023-11-16 21:27:48 -05:00
void FHttpRequestCommon : : SetStatus ( EHttpRequestStatus : : Type InCompletionStatus )
{
CompletionStatus = InCompletionStatus ;
2024-02-15 09:54:57 -05:00
if ( ResponseCommon )
2023-11-16 21:27:48 -05:00
{
ResponseCommon - > SetRequestStatus ( InCompletionStatus ) ;
}
}
2023-12-05 17:39:04 -05:00
void FHttpRequestCommon : : SetFailureReason ( EHttpFailureReason InFailureReason )
{
2024-02-27 19:20:09 -05:00
UE_CLOG ( FailureReason ! = EHttpFailureReason : : None , LogHttp , Warning , TEXT ( " FailureReason had been set to %s, now setting to %s " ) , LexToString ( FailureReason ) , LexToString ( InFailureReason ) ) ;
2023-12-05 17:39:04 -05:00
FailureReason = InFailureReason ;
2024-02-15 09:54:57 -05:00
if ( ResponseCommon )
2023-12-05 17:39:04 -05:00
{
ResponseCommon - > SetRequestFailureReason ( InFailureReason ) ;
}
2023-12-07 13:38:15 -05:00
}
void FHttpRequestCommon : : SetTimeout ( float InTimeoutSecs )
{
TimeoutSecs = InTimeoutSecs ;
}
void FHttpRequestCommon : : ClearTimeout ( )
{
TimeoutSecs . Reset ( ) ;
2024-02-08 16:31:09 -05:00
StopTotalTimeoutTimer ( ) ;
2023-12-07 13:38:15 -05:00
}
TOptional < float > FHttpRequestCommon : : GetTimeout ( ) const
{
return TimeoutSecs ;
}
float FHttpRequestCommon : : GetTimeoutOrDefault ( ) const
{
2024-01-23 16:08:08 -05:00
return GetTimeout ( ) . Get ( FHttpModule : : Get ( ) . GetHttpTotalTimeout ( ) ) ;
}
2024-02-15 09:54:57 -05:00
const FHttpResponsePtr FHttpRequestCommon : : GetResponse ( ) const
{
return ResponseCommon ;
}
2024-01-23 16:08:08 -05:00
void FHttpRequestCommon : : CancelRequest ( )
{
2024-02-27 19:20:09 -05:00
bool bWasCanceled = bCanceled . exchange ( true ) ;
if ( bWasCanceled )
2024-01-23 16:08:08 -05:00
{
return ;
}
2024-02-08 16:31:09 -05:00
StopActivityTimeoutTimer ( ) ;
2024-02-26 18:06:19 -05:00
StopPassingReceivedData ( ) ;
2024-01-23 16:08:08 -05:00
UE_LOG ( LogHttp , Verbose , TEXT ( " HTTP request canceled. URL=%s " ) , * GetURL ( ) ) ;
FHttpModule : : Get ( ) . GetHttpManager ( ) . AddHttpThreadTask ( [ StrongThis = StaticCastSharedRef < FHttpRequestCommon > ( AsShared ( ) ) ] ( )
{
// Run AbortRequest in HTTP thread to avoid potential concurrency issue
QUICK_SCOPE_CYCLE_COUNTER ( STAT_FHttpRequestCommon_AbortRequest ) ;
StrongThis - > AbortRequest ( ) ;
} ) ;
}
void FHttpRequestCommon : : StartActivityTimeoutTimer ( )
{
const FScopeLock CacheLock ( & HttpTaskTimerHandleCriticalSection ) ;
if ( bUsePlatformActivityTimeout )
{
return ;
}
# if !UE_BUILD_SHIPPING
static const bool bNoTimeouts = FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " NoTimeouts " ) ) ;
if ( bNoTimeouts )
{
return ;
}
# endif
if ( bActivityTimedOut )
{
return ;
}
float HttpActivityTimeout = FHttpModule : : Get ( ) . GetHttpActivityTimeout ( ) ;
2024-01-25 11:01:33 -05:00
if ( HttpActivityTimeout = = 0 )
{
return ;
}
2024-01-23 16:08:08 -05:00
StartActivityTimeoutTimerBy ( HttpActivityTimeout ) ;
ResetActivityTimeoutTimer ( TEXTVIEW ( " Connected " ) ) ;
}
void FHttpRequestCommon : : StartActivityTimeoutTimerBy ( double DelayToTrigger )
{
check ( ActivityTimeoutHttpTaskTimerHandle = = nullptr ) ;
TWeakPtr < IHttpRequest > RequestWeakPtr ( AsShared ( ) ) ;
ActivityTimeoutHttpTaskTimerHandle = FHttpModule : : Get ( ) . GetHttpManager ( ) . AddHttpThreadTask ( [ RequestWeakPtr ] ( ) {
if ( TSharedPtr < IHttpRequest > RequestPtr = RequestWeakPtr . Pin ( ) )
{
TSharedPtr < FHttpRequestCommon > RequestCommonPtr = StaticCastSharedPtr < FHttpRequestCommon > ( RequestPtr ) ;
RequestCommonPtr - > OnActivityTimeoutTimerTaskTrigger ( ) ;
}
} , DelayToTrigger + 0.05 ) ;
}
void FHttpRequestCommon : : OnActivityTimeoutTimerTaskTrigger ( )
{
const FScopeLock CacheLock ( & HttpTaskTimerHandleCriticalSection ) ;
ActivityTimeoutHttpTaskTimerHandle . Reset ( ) ;
2024-03-01 13:19:35 -05:00
if ( EHttpRequestStatus : : IsFinished ( GetStatus ( ) ) )
{
UE_LOG ( LogHttp , Warning , TEXT ( " Request %p had finished when activity timeout timer trigger at [%s] " ) , this , * FDateTime : : Now ( ) . ToString ( TEXT ( " %H:%M:%S:%s " ) ) ) ;
return ;
}
2024-01-23 16:08:08 -05:00
if ( FPlatformTime : : Seconds ( ) < ActivityTimeoutAt )
{
// Check back later
UE_LOG ( LogHttp , VeryVerbose , TEXT ( " Request %p check response timeout at [%s], will check again in %.5f seconds " ) , this , * FDateTime : : Now ( ) . ToString ( TEXT ( " %H:%M:%S:%s " ) ) , ActivityTimeoutAt - FPlatformTime : : Seconds ( ) ) ;
StartActivityTimeoutTimerBy ( ActivityTimeoutAt - FPlatformTime : : Seconds ( ) ) ;
return ;
}
QUICK_SCOPE_CYCLE_COUNTER ( STAT_FHttpRequestCommon_AbortRequest ) ;
bActivityTimedOut = true ;
AbortRequest ( ) ;
2024-01-26 14:25:18 -05:00
UE_LOG ( LogHttp , Log , TEXT ( " Request [%s] timed out at [%s] because of no responding for %0.2f seconds " ) , * GetURL ( ) , * FDateTime : : Now ( ) . ToString ( TEXT ( " %H:%M:%S:%s " ) ) , FHttpModule : : Get ( ) . GetHttpActivityTimeout ( ) ) ;
2024-01-23 16:08:08 -05:00
}
void FHttpRequestCommon : : ResetActivityTimeoutTimer ( FStringView Reason )
{
const FScopeLock CacheLock ( & HttpTaskTimerHandleCriticalSection ) ;
if ( bUsePlatformActivityTimeout )
{
return ;
}
2024-01-25 11:01:33 -05:00
if ( ! ActivityTimeoutHttpTaskTimerHandle )
{
return ;
}
2024-01-23 16:08:08 -05:00
ActivityTimeoutAt = FPlatformTime : : Seconds ( ) + FHttpModule : : Get ( ) . GetHttpActivityTimeout ( ) ;
UE_LOG ( LogHttp , VeryVerbose , TEXT ( " Request [%p] reset response timeout timer at %s: %s " ) , this , * FDateTime : : Now ( ) . ToString ( TEXT ( " %H:%M:%S:%s " ) ) , Reason . GetData ( ) ) ;
}
void FHttpRequestCommon : : StopActivityTimeoutTimer ( )
{
const FScopeLock CacheLock ( & HttpTaskTimerHandleCriticalSection ) ;
if ( bUsePlatformActivityTimeout )
{
return ;
}
2024-01-25 11:01:33 -05:00
if ( ! ActivityTimeoutHttpTaskTimerHandle )
2024-01-23 16:08:08 -05:00
{
2024-01-25 11:01:33 -05:00
return ;
2024-01-23 16:08:08 -05:00
}
2024-01-25 11:01:33 -05:00
FHttpModule : : Get ( ) . GetHttpManager ( ) . RemoveHttpThreadTask ( ActivityTimeoutHttpTaskTimerHandle ) ;
ActivityTimeoutHttpTaskTimerHandle . Reset ( ) ;
2024-01-23 16:08:08 -05:00
}
void FHttpRequestCommon : : StartTotalTimeoutTimer ( )
{
const FScopeLock CacheLock ( & HttpTaskTimerHandleCriticalSection ) ;
# if !UE_BUILD_SHIPPING
static const bool bNoTimeouts = FParse : : Param ( FCommandLine : : Get ( ) , TEXT ( " NoTimeouts " ) ) ;
if ( bNoTimeouts )
{
return ;
}
# endif
float TimeoutOrDefault = GetTimeoutOrDefault ( ) ;
if ( TimeoutOrDefault = = 0 )
{
return ;
}
if ( bTimedOut )
{
return ;
}
// Timeout include retries, so if it's already started before, check this to prevent from adding timer multiple times
if ( TotalTimeoutHttpTaskTimerHandle )
{
return ;
}
TWeakPtr < IHttpRequest > RequestWeakPtr ( AsShared ( ) ) ;
TotalTimeoutHttpTaskTimerHandle = FHttpModule : : Get ( ) . GetHttpManager ( ) . AddHttpThreadTask ( [ RequestWeakPtr ] ( ) {
if ( TSharedPtr < IHttpRequest > RequestPtr = RequestWeakPtr . Pin ( ) )
{
TSharedPtr < FHttpRequestCommon > RequestCommonPtr = StaticCastSharedPtr < FHttpRequestCommon > ( RequestPtr ) ;
RequestCommonPtr - > OnTotalTimeoutTimerTaskTrigger ( ) ;
}
} , TimeoutOrDefault ) ;
}
void FHttpRequestCommon : : OnTotalTimeoutTimerTaskTrigger ( )
{
const FScopeLock CacheLock ( & HttpTaskTimerHandleCriticalSection ) ;
bTimedOut = true ;
2024-03-01 13:19:35 -05:00
if ( EHttpRequestStatus : : IsFinished ( GetStatus ( ) ) )
2024-01-23 16:08:08 -05:00
{
2024-03-01 13:19:35 -05:00
return ;
2024-01-23 16:08:08 -05:00
}
2024-03-01 13:19:35 -05:00
StopActivityTimeoutTimer ( ) ;
QUICK_SCOPE_CYCLE_COUNTER ( STAT_FHttpRequestCommon_AbortRequest ) ;
UE_LOG ( LogHttp , Warning , TEXT ( " HTTP request timed out after %0.2f seconds URL=%s " ) , GetTimeoutOrDefault ( ) , * GetURL ( ) ) ;
AbortRequest ( ) ;
2024-01-23 16:08:08 -05:00
}
void FHttpRequestCommon : : StopTotalTimeoutTimer ( )
{
const FScopeLock CacheLock ( & HttpTaskTimerHandleCriticalSection ) ;
if ( TotalTimeoutHttpTaskTimerHandle )
{
FHttpModule : : Get ( ) . GetHttpManager ( ) . RemoveHttpThreadTask ( TotalTimeoutHttpTaskTimerHandle ) ;
TotalTimeoutHttpTaskTimerHandle . Reset ( ) ;
}
}
2024-02-08 16:31:09 -05:00
2024-01-23 16:08:08 -05:00
void FHttpRequestCommon : : Shutdown ( )
{
FHttpRequestImpl : : Shutdown ( ) ;
2024-02-26 18:06:19 -05:00
StopPassingReceivedData ( ) ;
2024-01-23 16:08:08 -05:00
StopActivityTimeoutTimer ( ) ;
StopTotalTimeoutTimer ( ) ;
2023-12-07 13:38:15 -05:00
}
2024-01-19 15:35:30 -05:00
2024-01-26 14:25:18 -05:00
void FHttpRequestCommon : : ProcessRequestUntilComplete ( )
{
checkf ( ! OnProcessRequestComplete ( ) . IsBound ( ) , TEXT ( " OnProcessRequestComplete is not supported for sync call " ) ) ;
SetDelegateThreadPolicy ( EHttpRequestDelegateThreadPolicy : : CompleteOnHttpThread ) ;
FEvent * Event = FPlatformProcess : : GetSynchEventFromPool ( true ) ;
OnProcessRequestComplete ( ) . BindLambda ( [ Event ] ( FHttpRequestPtr HttpRequest , FHttpResponsePtr HttpResponse , bool bSucceeded ) {
Event - > Trigger ( ) ;
} ) ;
ProcessRequest ( ) ;
Event - > Wait ( ) ;
FPlatformProcess : : ReturnSynchEventToPool ( Event ) ;
}
2024-01-19 15:35:30 -05:00
void FHttpRequestCommon : : TriggerStatusCodeReceivedDelegate ( int32 StatusCode )
{
if ( DelegateThreadPolicy = = EHttpRequestDelegateThreadPolicy : : CompleteOnHttpThread )
{
OnStatusCodeReceived ( ) . ExecuteIfBound ( SharedThis ( this ) , StatusCode ) ;
}
else if ( OnStatusCodeReceived ( ) . IsBound ( ) )
{
FHttpModule : : Get ( ) . GetHttpManager ( ) . AddGameThreadTask ( [ StrongThis = AsShared ( ) , StatusCode ] ( )
{
StrongThis - > OnStatusCodeReceived ( ) . ExecuteIfBound ( StrongThis , StatusCode ) ;
} ) ;
}
}
2024-02-05 13:23:49 -05:00
void FHttpRequestCommon : : SetEffectiveURL ( const FString & InEffectiveURL )
{
EffectiveURL = InEffectiveURL ;
2024-02-15 09:54:57 -05:00
if ( ResponseCommon )
2024-02-05 13:23:49 -05:00
{
ResponseCommon - > SetEffectiveURL ( EffectiveURL ) ;
}
}
2024-02-26 18:06:19 -05:00
bool FHttpRequestCommon : : SetResponseBodyReceiveStream ( TSharedRef < FArchive > Stream )
{
const FScopeLock StreamLock ( & ResponseBodyReceiveStreamCriticalSection ) ;
ResponseBodyReceiveStream = Stream ;
bInitializedWithValidStream = true ;
return true ;
}
bool FHttpRequestCommon : : PassReceivedDataToStream ( void * Ptr , int64 Length )
{
const FScopeLock StreamLock ( & ResponseBodyReceiveStreamCriticalSection ) ;
if ( ! ResponseBodyReceiveStream )
{
return false ;
}
ResponseBodyReceiveStream - > Serialize ( Ptr , Length ) ;
return ! ResponseBodyReceiveStream - > GetError ( ) ;
}
void FHttpRequestCommon : : StopPassingReceivedData ( )
{
const FScopeLock StreamLock ( & ResponseBodyReceiveStreamCriticalSection ) ;
ResponseBodyReceiveStream = nullptr ;
}