You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2814968 on 2016/01/04 by John.Pollard
Remove warning per connection when same warning happens at net driver level
Change 2815271 on 2016/01/04 by John.Pollard
Fix header uploads in replays showing wrong size
#rb none
#tests Compiled, confirmed correct size
Change 2830613 on 2016/01/15 by John.Barrett
Added better ability for unit tests to test socket-level netcode
Full changes:
- Added flag for auto-reconnecting on connection close
- Added hook for socket level sends
- Added ability to block socket-level sends and pre-PacketHandler LowLevelSend's
- Added ability for net connections to force-enable packet handlers, even if presently off in the engine
#rb none
Change 2830617 on 2016/01/15 by John.Barrett@JC2Q-LT_UE4
Enabled NetcodeUnitTest in Linux, and other minor updates.
#rb none
Change 2830621 on 2016/01/15 by John.Barrett@JC2Q-LT_UE4
Added 'PacketLimitTest' and 'PacketLimitTest_Oodle' unit tests, for testing/verifying that MaxPacket limits are respected, through testing with Bunches and with raw sends passed through PacketHandlers (such as Oodle).
#rb none
Change 2832402 on 2016/01/18 by Bart.Bressler@bart.bressler_Dev_Networking
Always use the physical platform file when writing out steam_appid.txt, because it has to live next to executable that's being run (CotF builds will output it to the wrong location).
#rb josh.markiewicz
#tests checked that steam_appid.txt was written to the correct place with both CotF and cooked builds
Change 2833728 on 2016/01/19 by Joe.Conley@Joe.Conley_EGJWD5708_Dev-Networking-Minimal
Fix for UE-20017 - "Http server headers implemented as TMaps and are overridden". Just changing TMaps to TMultiMaps. Haven't modified the behavior of GetHeader(), which will still return the first header that matches.
Change 2844880 on 2016/01/26 by Joe.Conley
Fix for UE-20017 Http server headers implemented as TMaps and are overridden
Previously had submitted a fix that changes the request and response headers TMap into a MultiMap, but the consensus was that it would be better to leave them as TMaps, but allow appending onto the existing entry for a given request header with an "AppendToHeader" function, and to concatenate entries for the same header name in the response headers. In both cases, entries for the same header name are concatenated with ", ".
Mac and iOS already had the desired behavior for responses, just added the AppendToHeader function to their request classes.
HTML5 still has no implementation of response headers, filed as separate JIRA issue UE-26047.
#platformnotify josh.adams
Change 2859343 on 2016/02/08 by Bart.Bressler
Default MaxSearchResults in FOnlineSessionSearch objects to 1 instead of 0 - otherwise, the FOnlineAsyncTaskSteamFindServerBase tasks will complete right away without searching for sessions. Also print a warning in the task if this will happen.
#codereview josh.markiewicz
Change 2862352 on 2016/02/10 by Bart.Bressler
Fix demo.SkipTime cvar to work with negative values
#rb john.pollard
Change 2866624 on 2016/02/14 by John.Barrett
Fix plugin commandlet crash.
#rb none
Change 2866627 on 2016/02/14 by John.Barrett
Added NetDriver/PacketHandler ability to send/receive connectionless packets - i.e. packets not associated with a UNetConnection.
Removed 'bEnabled' from PacketHandler - PacketHandler is now a default always-on part of the Engine.
Added 'CountBits' to LowLevelSend, to support bit-level additions to packets from PacketHandler's, and refactored related code.
#codereview John.Pollard
Change 2866628 on 2016/02/14 by John.Barrett
Added an HMAC (Hash-based Message Authentication Code) generating function, to the SHA-1 code - for use with performing secure net connection handshakes.
#rb none
Change 2866629 on 2016/02/14 by John.Barrett
Added 'stateless handshake' to UE4, allowing a handshake to be performed without any open UNetConnection - making it more difficult for DoS attacks to consume memory resources.
#codereview John.Pollard
Change 2866630 on 2016/02/14 by John.Barrett
Updated Oodle to support new stateless handshake.
#rb none
Change 2866631 on 2016/02/14 by John.Barrett
Updated NetcodeUnitTest to work with new connectionless/stateless handshake changes. Compiles, but netcode presently needs fixing.
#rb none
Change 2868367 on 2016/02/16 by John.Barrett
Updated NetcodeUnitTest to work with stateless handshakes (now fixed connecting).
Also fixed further commandlet problems.
#rb none
Change 2878025 on 2016/02/23 by John.Barrett
Immediately stop processing zero-size packets, to prevent CPU utilization during DDoS.
Log IP of new connections, post-stateless-challenge (first time we know IP is verified).
#codereview john.pollard
Change 2883776 on 2016/02/26 by John.Barrett
Fixed beacons to work with stateless handshake.
Change 2886773 on 2016/02/29 by John.Barrett
Made PacketHandler bit overhead align upwards to the next byte - this wastes more bits than necessary, but is required as a temporary fix.
Change 2887801 on 2016/03/01 by John.Pollard
Use correct default values when initializing shadow state for replication.
Fixes issues with certain components and actors that have default values that differ from CDO.
[CL 2901490 by John Pollard in Main branch]
678 lines
17 KiB
C++
678 lines
17 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "HttpPrivatePCH.h"
|
|
#include "HTML5HTTP.h"
|
|
#include "EngineVersion.h"
|
|
|
|
#if PLATFORM_HTML5_BROWSER
|
|
#include "HTML5JavaScriptFx.h"
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* FHTML5HttpRequest implementation
|
|
***************************************************************************/
|
|
|
|
FHTML5HttpRequest::FHTML5HttpRequest()
|
|
: bCanceled(false)
|
|
, bCompleted(false)
|
|
, BytesSent(0)
|
|
, CompletionStatus(EHttpRequestStatus::NotStarted)
|
|
, ElapsedTime(0.0f)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::FHTML5HttpRequest()"));
|
|
}
|
|
|
|
|
|
FHTML5HttpRequest::~FHTML5HttpRequest()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::~FHTML5HttpRequest()"));
|
|
}
|
|
|
|
|
|
FString FHTML5HttpRequest::GetURL()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::GetURL() - %s"), *URL);
|
|
return URL;
|
|
}
|
|
|
|
|
|
void FHTML5HttpRequest::SetURL(const FString& InURL)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::SetURL() - %s"), *InURL);
|
|
URL = InURL;
|
|
}
|
|
|
|
|
|
FString FHTML5HttpRequest::GetURLParameter(const FString& ParameterName)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::GetURLParameter() - %s"), *ParameterName);
|
|
|
|
TArray<FString> StringElements;
|
|
|
|
int32 NumElems = URL.ParseIntoArray(StringElements, TEXT("&"), true);
|
|
check(NumElems == StringElements.Num());
|
|
|
|
FString ParamValDelimiter(TEXT("="));
|
|
for (int Idx = 0; Idx < NumElems; ++Idx )
|
|
{
|
|
FString Param, Value;
|
|
if (StringElements[Idx].Split(ParamValDelimiter, &Param, &Value) && Param == ParameterName)
|
|
{
|
|
return Value;
|
|
}
|
|
}
|
|
|
|
return FString();
|
|
}
|
|
|
|
|
|
FString FHTML5HttpRequest::GetHeader(const FString& HeaderName)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::GetHeader() - %s"), *HeaderName);
|
|
|
|
FString Result;
|
|
|
|
FString* Header = Headers.Find(HeaderName);
|
|
|
|
if (Header != NULL)
|
|
{
|
|
Result = *Header;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
void FHTML5HttpRequest::SetHeader(const FString& HeaderName, const FString& HeaderValue)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::SetHeader() - %s / %s"), *HeaderName, *HeaderValue);
|
|
Headers.Add(HeaderName, HeaderValue);
|
|
}
|
|
|
|
|
|
TArray<FString> FHTML5HttpRequest::GetAllHeaders()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::GetAllHeaders()"));
|
|
|
|
TArray<FString> Result;
|
|
for (TMap<FString, FString>::TConstIterator It(Headers); It; ++It)
|
|
{
|
|
Result.Add(It.Key() + TEXT(": ") + It.Value());
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
|
|
const TArray<uint8>& FHTML5HttpRequest::GetContent()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::GetContent()"));
|
|
|
|
return RequestPayload;
|
|
}
|
|
|
|
|
|
void FHTML5HttpRequest::SetContent(const TArray<uint8>& ContentPayload)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::SetContent()"));
|
|
|
|
RequestPayload = ContentPayload;
|
|
}
|
|
|
|
|
|
FString FHTML5HttpRequest::GetContentType()
|
|
{
|
|
return GetHeader(TEXT( "Content-Type" ));
|
|
}
|
|
|
|
|
|
int32 FHTML5HttpRequest::GetContentLength()
|
|
{
|
|
int Len = RequestPayload.Num();
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::GetContentLength() - %i"), Len);
|
|
|
|
return Len;
|
|
}
|
|
|
|
|
|
void FHTML5HttpRequest::SetContentAsString(const FString& ContentString)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::SetContentAsString() - %s"), *ContentString);
|
|
|
|
FTCHARToUTF8 Converter(*ContentString);
|
|
RequestPayload.SetNum(Converter.Length());
|
|
FMemory::Memcpy(RequestPayload.GetData(), (uint8*)(ANSICHAR*)Converter.Get(), RequestPayload.Num());
|
|
}
|
|
|
|
|
|
void FHTML5HttpRequest::AppendToHeader(const FString& HeaderName, const FString& AdditionalHeaderValue)
|
|
{
|
|
if (!HeaderName.IsEmpty() && !AdditionalHeaderValue.IsEmpty())
|
|
{
|
|
FString* PreviousValue = Headers.Find(HeaderName);
|
|
FString NewValue;
|
|
if (PreviousValue != nullptr && !PreviousValue->IsEmpty())
|
|
{
|
|
NewValue = (*PreviousValue) + TEXT(", ");
|
|
}
|
|
NewValue += AdditionalHeaderValue;
|
|
|
|
SetHeader(HeaderName, NewValue);
|
|
}
|
|
}
|
|
|
|
FString FHTML5HttpRequest::GetVerb()
|
|
{
|
|
return Verb;
|
|
}
|
|
|
|
|
|
void FHTML5HttpRequest::SetVerb(const FString& InVerb)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::SetVerb() - %s"), *InVerb);
|
|
|
|
Verb = InVerb.ToUpper();
|
|
}
|
|
|
|
bool IsURLEncoded(const TArray<uint8> & Payload)
|
|
{
|
|
static char AllowedChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~";
|
|
static bool bTableFilled = false;
|
|
static bool AllowedTable[256] = { false };
|
|
|
|
if (!bTableFilled)
|
|
{
|
|
for (int32 Idx = 0; Idx < ARRAY_COUNT(AllowedChars) - 1; ++Idx) // -1 to avoid trailing 0
|
|
{
|
|
uint8 AllowedCharIdx = static_cast<uint8>(AllowedChars[Idx]);
|
|
check(AllowedCharIdx < ARRAY_COUNT(AllowedTable));
|
|
AllowedTable[AllowedCharIdx] = true;
|
|
}
|
|
|
|
bTableFilled = true;
|
|
}
|
|
|
|
const int32 Num = Payload.Num();
|
|
for (int32 Idx = 0; Idx < Num; ++Idx)
|
|
{
|
|
if (!AllowedTable[Payload[Idx]])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FHTML5HttpRequest::StaticReceiveCallback(void *arg, void *buffer, uint32 size){
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::StaticReceiveDataCallback()"));
|
|
|
|
FHTML5HttpRequest* Request = reinterpret_cast<FHTML5HttpRequest*>(arg);
|
|
|
|
return Request->ReceiveCallback(arg, buffer, size);
|
|
}
|
|
|
|
void FHTML5HttpRequest::ReceiveCallback(void *arg, void *buffer, uint32 size) {
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::ReceiveDataCallback()"));
|
|
UE_LOG(LogHttp, Verbose, TEXT("Response size: %d"), size);
|
|
|
|
check(Response.IsValid());
|
|
|
|
if (Response.IsValid())
|
|
{
|
|
Response->Payload.AddUninitialized(size);
|
|
|
|
// save
|
|
UE_LOG(LogHttp, Verbose, TEXT("Saving payload..."));
|
|
|
|
FMemory::Memcpy(static_cast<uint8*>(Response->Payload.GetData()), buffer, size);
|
|
Response->TotalBytesRead = size;
|
|
Response->HttpCode = 200;
|
|
|
|
UE_LOG(LogHttp, Verbose, TEXT("Payload length: %d"), Response->Payload.Num());
|
|
|
|
MarkAsCompleted();
|
|
}
|
|
}
|
|
|
|
void FHTML5HttpRequest::StaticErrorCallback(void* arg, int httpStatusCode, const char* httpStatusText) {
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::StaticErrorDataCallback()"));
|
|
|
|
FHTML5HttpRequest* Request = reinterpret_cast<FHTML5HttpRequest*>(arg);
|
|
return Request->ErrorCallback(arg, httpStatusCode, httpStatusText);
|
|
}
|
|
|
|
void FHTML5HttpRequest::ErrorCallback(void* arg, int httpStatusCode, const char* httpStatusText) {
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::ErrorDataCallback() HttpStatusCode: %d"), httpStatusCode);
|
|
|
|
check(Response.IsValid());
|
|
|
|
if (Response.IsValid())
|
|
{
|
|
Response->Payload.Empty();
|
|
Response->TotalBytesRead = 0;
|
|
Response->HttpCode = httpStatusCode;
|
|
MarkAsCompleted();
|
|
}
|
|
|
|
}
|
|
|
|
void FHTML5HttpRequest::StaticProgressCallback(void* arg, int Loaded, int Total) {
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::StaticProgressCallback()"));
|
|
|
|
FHTML5HttpRequest* Request = reinterpret_cast<FHTML5HttpRequest*>(arg);
|
|
|
|
return Request->ProgressCallback(arg, Loaded, Total);
|
|
}
|
|
|
|
void FHTML5HttpRequest::ProgressCallback(void* arg, int Loaded, int Total) {
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::ProgressCallback()"));
|
|
|
|
if (GetVerb() == TEXT("GET")) {
|
|
if (Response.IsValid())
|
|
{
|
|
Response->TotalBytesRead = Loaded;
|
|
OnRequestProgress().ExecuteIfBound(SharedThis(this), 0, Response->TotalBytesRead);
|
|
}
|
|
}
|
|
else {
|
|
BytesSent = Loaded;
|
|
OnRequestProgress().ExecuteIfBound(SharedThis(this), BytesSent, 0);
|
|
}
|
|
|
|
UE_LOG(LogHttp, Verbose, TEXT("Loaded: %d, Total: %d"), Loaded, Total);
|
|
}
|
|
|
|
|
|
bool FHTML5HttpRequest::StartRequest()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::StartRequest()"));
|
|
|
|
UE_LOG(LogHttp, Verbose, TEXT("%p: URL='%s'"), this, *URL);
|
|
UE_LOG(LogHttp, Verbose, TEXT("%p: Verb='%s'"), this, *Verb);
|
|
UE_LOG(LogHttp, Verbose, TEXT("%p: Custom headers are %s"), this, Headers.Num() ? TEXT("present") : TEXT("NOT present"));
|
|
UE_LOG(LogHttp, Verbose, TEXT("%p: Payload size=%d"), this, RequestPayload.Num());
|
|
|
|
if (!FHttpModule::Get().IsHttpEnabled())
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("Http disabled. Skipping request. url=%s"), *GetURL());
|
|
return false;
|
|
}// Prevent overlapped requests using the same instance
|
|
else if (CompletionStatus == EHttpRequestStatus::Processing)
|
|
{
|
|
UE_LOG(LogHttp, Warning, TEXT("ProcessRequest failed. Still processing last request."));
|
|
return false;
|
|
}
|
|
// Nothing to do without a valid URL
|
|
else if (URL.IsEmpty())
|
|
{
|
|
UE_LOG(LogHttp, Log, TEXT("Cannot process HTTP request: URL is empty"));
|
|
return false;
|
|
}
|
|
|
|
|
|
//"User-Agent" && "Content-Length" are automatically set by the browser xhr request. We can't do much.
|
|
|
|
// make a fake header, so server has some idea this is UE
|
|
SetHeader(TEXT("X-UnrealEngine-Agent"), FString::Printf(TEXT("game=%s, engine=UE4, version=%s"), FApp::GetGameName(), *FEngineVersion::Current().ToString()));
|
|
|
|
// Add "Pragma: no-cache" to mimic WinInet behavior
|
|
if (GetHeader("Pragma").IsEmpty())
|
|
{
|
|
SetHeader(TEXT("Pragma"), TEXT("no-cache"));
|
|
}
|
|
|
|
TArray<FString> AllHeaders = GetAllHeaders();
|
|
|
|
// Create a String which emscripten can understand.
|
|
FString RequestHeaders = FString::Join(AllHeaders, TEXT("%"));
|
|
|
|
// set up verb (note that Verb is expected to be uppercase only)
|
|
if (Verb == TEXT("POST"))
|
|
{
|
|
// If we don't pass any other Content-Type, RequestPayload is assumed to be URL-encoded by this time
|
|
// (if we pass, don't check here and trust the request)
|
|
check(!GetHeader("Content-Type").IsEmpty() || IsURLEncoded(RequestPayload));
|
|
|
|
#if PLATFORM_HTML5_BROWSER
|
|
UE_MakeHTTPDataRequest(this, TCHAR_TO_ANSI(*URL), "POST", (char*)RequestPayload.GetData(), RequestPayload.Num(),TCHAR_TO_ANSI(*RequestHeaders), 0, StaticReceiveCallback, StaticErrorCallback, StaticProgressCallback);
|
|
#else
|
|
return false;
|
|
#endif
|
|
|
|
}
|
|
else if (Verb == TEXT("PUT"))
|
|
{
|
|
UE_LOG(LogHttp, Log, TEXT("TODO: PUT"));
|
|
|
|
//TODO: PUT
|
|
|
|
// reset the counter
|
|
BytesSent = 0;
|
|
return false;
|
|
}
|
|
else if (Verb == TEXT("GET"))
|
|
{
|
|
#if PLATFORM_HTML5_BROWSER
|
|
UE_MakeHTTPDataRequest(this, TCHAR_TO_ANSI(*URL), "GET", NULL, 0,TCHAR_TO_ANSI(*RequestHeaders), 1, StaticReceiveCallback, StaticErrorCallback, StaticProgressCallback);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
else if (Verb == TEXT("HEAD"))
|
|
{
|
|
UE_LOG(LogHttp, Log, TEXT("TODO: HEAD"));
|
|
|
|
//TODO: HEAD
|
|
return false;
|
|
}
|
|
else if (Verb == TEXT("DELETE"))
|
|
{
|
|
|
|
// If we don't pass any other Content-Type, RequestPayload is assumed to be URL-encoded by this time
|
|
// (if we pass, don't check here and trust the request)
|
|
check(!GetHeader("Content-Type").IsEmpty() || IsURLEncoded(RequestPayload));
|
|
|
|
//TODO: DELETE
|
|
|
|
UE_LOG(LogHttp, Log, TEXT("TODO: DELETE"));
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogHttp, Fatal, TEXT("Unsupported verb '%s"), *Verb);
|
|
FPlatformMisc::DebugBreak();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FHTML5HttpRequest::ProcessRequest()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::ProcessRequest()"));
|
|
|
|
if (!StartRequest())
|
|
{
|
|
UE_LOG(LogHttp, Warning, TEXT("Processing HTTP request failed. Increase verbosity for additional information."));
|
|
|
|
// No response since connection failed
|
|
Response = NULL;
|
|
// Cleanup and call delegate
|
|
FinishedRequest();
|
|
|
|
return false;
|
|
}
|
|
|
|
// Mark as in-flight to prevent overlapped requests using the same object
|
|
CompletionStatus = EHttpRequestStatus::Processing;
|
|
// Response object to handle data that comes back after starting this request
|
|
Response = MakeShareable(new FHTML5HttpResponse(*this));
|
|
// Add to global list while being processed so that the ref counted request does not get deleted
|
|
FHttpModule::Get().GetHttpManager().AddRequest(SharedThis(this));
|
|
// reset timeout
|
|
ElapsedTime = 0.0f;
|
|
|
|
UE_LOG(LogHttp, Verbose, TEXT("Request is waiting for processing"), this );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
FHttpRequestCompleteDelegate& FHTML5HttpRequest::OnProcessRequestComplete()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::OnProcessRequestComplete()"));
|
|
return RequestCompleteDelegate;
|
|
}
|
|
|
|
FHttpRequestProgressDelegate& FHTML5HttpRequest::OnRequestProgress()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::OnRequestProgress()"));
|
|
return RequestProgressDelegate;
|
|
}
|
|
|
|
void FHTML5HttpRequest::FinishedRequest()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::FinishedRequest()"));
|
|
|
|
// if completed, get more info
|
|
if (bCompleted)
|
|
{
|
|
if (Response.IsValid())
|
|
{
|
|
Response->bSucceeded = EHttpResponseCodes::IsOk(Response->HttpCode);
|
|
Response->ContentLength = Response->TotalBytesRead;
|
|
}
|
|
}
|
|
|
|
// if just finished, mark as stopped async processing
|
|
if (Response.IsValid())
|
|
{
|
|
Response->bIsReady = true;
|
|
}
|
|
|
|
// Clean up session/request handles that may have been created
|
|
CleanupRequest();
|
|
|
|
if (Response.IsValid() &&
|
|
Response->bSucceeded)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("%p: request has been successfully processed. HTTP code: %d, content length: %d, actual payload size: %d"),
|
|
this, Response->HttpCode, Response->ContentLength, Response->Payload.Num() );
|
|
|
|
// Mark last request attempt as completed successfully
|
|
CompletionStatus = EHttpRequestStatus::Succeeded;
|
|
// Call delegate with valid request/response objects
|
|
OnProcessRequestComplete().ExecuteIfBound(SharedThis(this),Response,true);
|
|
}
|
|
else
|
|
{
|
|
// Mark last request attempt as completed but failed
|
|
CompletionStatus = EHttpRequestStatus::Failed;
|
|
// No response since connection failed
|
|
Response = NULL;
|
|
// Call delegate with failure
|
|
OnProcessRequestComplete().ExecuteIfBound(SharedThis(this),NULL,false);
|
|
}
|
|
// Remove from global list since processing is now complete
|
|
FHttpModule::Get().GetHttpManager().RemoveRequest(SharedThis(this));
|
|
}
|
|
|
|
|
|
void FHTML5HttpRequest::CleanupRequest()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::CleanupRequest()"));
|
|
if (CompletionStatus == EHttpRequestStatus::Processing)
|
|
{
|
|
CancelRequest();
|
|
}
|
|
}
|
|
|
|
|
|
void FHTML5HttpRequest::CancelRequest()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::CancelRequest()"));
|
|
bCanceled = true;
|
|
}
|
|
|
|
|
|
EHttpRequestStatus::Type FHTML5HttpRequest::GetStatus()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::GetStatus()"));
|
|
return CompletionStatus;
|
|
}
|
|
|
|
const FHttpResponsePtr FHTML5HttpRequest::GetResponse() const
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpRequest::GetResponse()"));
|
|
return Response;
|
|
}
|
|
|
|
|
|
void FHTML5HttpRequest::Tick(float DeltaSeconds)
|
|
{
|
|
// check for true completion/cancellation
|
|
if (bCompleted || bCanceled)
|
|
{
|
|
FinishedRequest();
|
|
return;
|
|
}
|
|
|
|
// keep track of elapsed seconds
|
|
ElapsedTime += DeltaSeconds;
|
|
const float HttpTimeout = FHttpModule::Get().GetHttpTimeout();
|
|
if (HttpTimeout > 0 && ElapsedTime >= HttpTimeout)
|
|
{
|
|
UE_LOG(LogHttp, Warning, TEXT("Timeout processing Http request. %p"),
|
|
this);
|
|
|
|
// finish it off since it is timeout
|
|
FinishedRequest();
|
|
}
|
|
}
|
|
|
|
float FHTML5HttpRequest::GetElapsedTime()
|
|
{
|
|
return ElapsedTime;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* FHTML5HttpResponse implementation
|
|
**************************************************************************/
|
|
|
|
FHTML5HttpResponse::FHTML5HttpResponse(FHTML5HttpRequest& InRequest)
|
|
: Request(InRequest)
|
|
, TotalBytesRead(0)
|
|
, HttpCode(EHttpResponseCodes::Unknown)
|
|
, ContentLength(0)
|
|
, bIsReady(0)
|
|
, bSucceeded(0)
|
|
{
|
|
}
|
|
|
|
|
|
FHTML5HttpResponse::~FHTML5HttpResponse()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpResponse::~FHTML5HttpResponse()"));
|
|
}
|
|
|
|
|
|
FString FHTML5HttpResponse::GetURL()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpResponse::GetURL()"));
|
|
return Request.GetURL();
|
|
}
|
|
|
|
|
|
FString FHTML5HttpResponse::GetURLParameter(const FString& ParameterName)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpResponse::GetURLParameter()"));
|
|
|
|
return Request.GetURLParameter(ParameterName);
|
|
}
|
|
|
|
|
|
FString FHTML5HttpResponse::GetHeader(const FString& HeaderName)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpResponse::GetHeader()"));
|
|
|
|
FString Result(TEXT(""));
|
|
|
|
if (!bIsReady)
|
|
{
|
|
UE_LOG(LogHttp, Warning, TEXT("Can't get cached header [%s]. Response still processing. %p"),
|
|
*HeaderName, &Request);
|
|
}
|
|
else
|
|
{
|
|
FString* Header = Headers.Find(HeaderName);
|
|
|
|
if (Header != NULL)
|
|
{
|
|
return *Header;
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
TArray<FString> FHTML5HttpResponse::GetAllHeaders()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpResponse::GetAllHeaders()"));
|
|
|
|
TArray<FString> Result;
|
|
|
|
if (!bIsReady)
|
|
{
|
|
UE_LOG(LogHttp, Warning, TEXT("Can't get cached headers. Response still processing. %p"),&Request);
|
|
}
|
|
else
|
|
{
|
|
for (TMap<FString, FString>::TConstIterator It(Headers); It; ++It)
|
|
{
|
|
Result.Add(It.Key() + TEXT(": ") + It.Value());
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
FString FHTML5HttpResponse::GetContentType()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpResponse::GetContentType()"));
|
|
|
|
return GetHeader(TEXT("Content-Type"));
|
|
}
|
|
|
|
|
|
int32 FHTML5HttpResponse::GetContentLength()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpResponse::GetContentLength()"));
|
|
|
|
return ContentLength;
|
|
}
|
|
|
|
|
|
const TArray<uint8>& FHTML5HttpResponse::GetContent()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpResponse::GetContent()"));
|
|
|
|
if (!IsReady())
|
|
{
|
|
UE_LOG(LogHttp, Warning, TEXT("Payload is incomplete. Response still processing. %p"), &Request);
|
|
}
|
|
|
|
return Payload;
|
|
}
|
|
|
|
|
|
FString FHTML5HttpResponse::GetContentAsString()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpResponse::GetContentAsString()"));
|
|
|
|
// Fill in our data.
|
|
GetContent();
|
|
|
|
TArray<uint8> ZeroTerminatedPayload;
|
|
ZeroTerminatedPayload.AddZeroed(Payload.Num() + 1);
|
|
FMemory::Memcpy(ZeroTerminatedPayload.GetData(), Payload.GetData(), Payload.Num());
|
|
|
|
return UTF8_TO_TCHAR(ZeroTerminatedPayload.GetData());
|
|
}
|
|
|
|
|
|
int32 FHTML5HttpResponse::GetResponseCode()
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpResponse::GetResponseCode()"));
|
|
|
|
return HttpCode;
|
|
}
|
|
|
|
bool FHTML5HttpResponse::IsReady()
|
|
{
|
|
if (bIsReady)
|
|
{
|
|
UE_LOG(LogHttp, Verbose, TEXT("FHTML5HttpResponse::IsReady()"));
|
|
}
|
|
|
|
return bIsReady;
|
|
} |