Files
UnrealEngine/Engine/Source/Runtime/Experimental/IoStore/HttpClient/Private/Api.inl
2025-05-18 13:04:45 +08:00

305 lines
8.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
namespace UE::IoStore::HTTP
{
// {{{1 request ................................................................
////////////////////////////////////////////////////////////////////////////////
FRequest::~FRequest()
{
if (Ptr == nullptr)
{
return;
}
Activity_Free(Ptr);
}
////////////////////////////////////////////////////////////////////////////////
FRequest::FRequest(FRequest&& Rhs)
{
this->~FRequest();
Swap(Ptr, Rhs.Ptr);
}
////////////////////////////////////////////////////////////////////////////////
FRequest&& FRequest::Accept(EMimeType MimeType)
{
switch (MimeType)
{
case EMimeType::Text: return Header("Accept", "text/html");
case EMimeType::Binary: return Header("Accept", "application/octet-stream");
case EMimeType::Json: return Header("Accept", "application/json");
case EMimeType::Xml: return Header("Accept", "application/xml");
case EMimeType::CbObject: return Header("Accept", "application/x-ue-cb");
case EMimeType::CbPackage: return Header("Accept", "application/x-ue-pkg");
case EMimeType::CompressedBuffer: return Header("Accept", "application/x-ue-comp");
}
return MoveTemp(*this);
}
////////////////////////////////////////////////////////////////////////////////
FRequest&& FRequest::Accept(FAnsiStringView MimeType)
{
return Header("Accept", MimeType);
}
////////////////////////////////////////////////////////////////////////////////
FRequest&& FRequest::Header(FAnsiStringView Key, FAnsiStringView Value)
{
checkSlow(Ptr->State == FActivity::EState::Build);
Ptr->Buffer.AddHeader(Key, Value);
return MoveTemp(*this);
}
// {{{1 response ...............................................................
////////////////////////////////////////////////////////////////////////////////
EStatusCodeClass FResponse::GetStatus() const
{
uint32 Code = GetStatusCode();
if (Code <= 199) return EStatusCodeClass::Informational;
if (Code <= 299) return EStatusCodeClass::Successful;
if (Code <= 399) return EStatusCodeClass::Redirection;
if (Code <= 499) return EStatusCodeClass::ClientError;
if (Code <= 599) return EStatusCodeClass::ServerError;
return EStatusCodeClass::Unknown;
}
////////////////////////////////////////////////////////////////////////////////
uint32 FResponse::GetStatusCode() const
{
const auto* Activity = (const FActivity*)this;
const FResponseInternal& Internal = Activity->Response;
const char* MessageData = Activity->Buffer.GetData() + Activity->StateParam;
if (Internal.Code < 0)
{
const char* CodePtr = MessageData + Internal.Offsets.StatusCode;
Internal.Code = uint16(CrudeToInt(FAnsiStringView(CodePtr, 3)));
}
return Internal.Code;
}
////////////////////////////////////////////////////////////////////////////////
FAnsiStringView FResponse::GetStatusMessage() const
{
const auto* Activity = (const FActivity*)this;
const FResponseInternal& Internal = Activity->Response;
const char* MessageData = Activity->Buffer.GetData() + Activity->StateParam;
return FAnsiStringView(
MessageData + Internal.Offsets.Message,
Internal.Offsets.Headers - Internal.Offsets.Message
);
}
////////////////////////////////////////////////////////////////////////////////
int64 FResponse::GetContentLength() const
{
const auto* Activity = (const FActivity*)this;
const FResponseInternal& Internal = Activity->Response;
return Internal.ContentLength;
}
////////////////////////////////////////////////////////////////////////////////
EMimeType FResponse::GetContentType() const
{
FAnsiStringView Value;
GetContentType(Value);
if (Value == "text/html") return EMimeType::Text;
if (Value == "application/octet-stream") return EMimeType::Binary;
if (Value == "application/json") return EMimeType::Json;
if (Value == "application/xml") return EMimeType::Xml;
if (Value == "application/x-ue-cb") return EMimeType::CbObject;
if (Value == "application/x-ue-pkg") return EMimeType::CbPackage;
if (Value == "application/x-ue-comp") return EMimeType::CompressedBuffer;
return EMimeType::Unknown;
}
////////////////////////////////////////////////////////////////////////////////
void FResponse::GetContentType(FAnsiStringView& Out) const
{
Out = GetHeader("Content-Type");
int32 SemiColon;
if (Out.FindChar(';', SemiColon))
{
Out = Out.Mid(SemiColon).TrimEnd();
}
}
////////////////////////////////////////////////////////////////////////////////
FAnsiStringView FResponse::GetHeader(FAnsiStringView Name) const
{
FAnsiStringView Result;
ReadHeaders([&Result, Name] (FAnsiStringView Candidate, FAnsiStringView Value)
{
if (Candidate != Name)
{
return true;
}
Result = Value;
return false;
});
return Result;
}
////////////////////////////////////////////////////////////////////////////////
void FResponse::ReadHeaders(FHeaderSink Sink) const
{
const auto* Activity = (const FActivity*)this;
const FResponseInternal& Internal = Activity->Response;
const char* MessageData = Activity->Buffer.GetData() + Activity->StateParam;
FAnsiStringView Result, Headers(
MessageData + Internal.Offsets.Headers,
Internal.MessageLength - Internal.Offsets.Headers
);
EnumerateHeaders(Headers, Sink);
}
////////////////////////////////////////////////////////////////////////////////
void FResponse::SetDestination(FIoBuffer* Buffer)
{
auto* Activity = (FActivity*)this;
Activity->Dest = Buffer;
}
// {{{1 ticket-status ..........................................................
////////////////////////////////////////////////////////////////////////////////
FTicketStatus::EId FTicketStatus::GetId() const
{
const auto* Activity = (FActivity*)this;
switch (Activity->State)
{
case FActivity::EState::RecvMessage: return EId::Response;
case FActivity::EState::RecvStream:
case FActivity::EState::RecvDone: return EId::Content;
case FActivity::EState::Cancelled: return EId::Cancelled;
case FActivity::EState::Failed: return EId::Error;
default: check(false);
}
return EId::Error;
}
////////////////////////////////////////////////////////////////////////////////
UPTRINT FTicketStatus::GetParam() const
{
const auto* Activity = (FActivity*)this;
return Activity->SinkParam;
}
////////////////////////////////////////////////////////////////////////////////
FTicket FTicketStatus::GetTicket() const
{
const auto* Activity = (FActivity*)this;
return 1ull << Activity->Slot;
}
////////////////////////////////////////////////////////////////////////////////
uint32 FTicketStatus::GetIndex() const
{
const auto* Activity = (FActivity*)this;
return Activity->Slot;
}
////////////////////////////////////////////////////////////////////////////////
FResponse& FTicketStatus::GetResponse() const
{
check(GetId() < EId::Content);
const auto* Activity = (FActivity*)this;
return *(FResponse*)Activity;
}
////////////////////////////////////////////////////////////////////////////////
uint32 FTicketStatus::GetContentLength() const
{
check(GetId() <= EId::Content);
const auto* Activity = (FActivity*)this;
return Activity->Response.ContentLength;
}
////////////////////////////////////////////////////////////////////////////////
const FTicketPerf& FTicketStatus::GetPerf() const
{
check(GetId() == EId::Content);
const auto* Activity = (FActivity*)this;
return *(FTicketPerf*)Activity;
}
////////////////////////////////////////////////////////////////////////////////
const FIoBuffer& FTicketStatus::GetContent() const
{
check(GetId() == EId::Content);
const auto* Activity = (FActivity*)this;
return *(Activity->Dest);
}
////////////////////////////////////////////////////////////////////////////////
FTicketStatus::FError FTicketStatus::GetError() const
{
check(GetId() == EId::Error);
const auto* Activity = (FActivity*)this;
return FError({Activity->ErrorReason, Activity->StateParam});
}
// {{{1 perf ...................................................................
#if IAS_HTTP_WITH_PERF
////////////////////////////////////////////////////////////////////////////////
FTicketPerf::FSample FTicketPerf::GetSample() const
{
const auto& Activity = *(FActivity*)this;
static uint64 Freq;
if (Freq == 0)
{
Freq = uint64(1.0 / FPlatformTime::GetSecondsPerCycle64());
}
auto Clamp = [] (auto Value) { return uint16(FMath::Min(uint32(Value), 0xffffu)); };
auto ToMs = [&] (uint64 Value) { return Clamp((Value * 1000ull) / Freq); };
const FStopwatch& Stopwatch = Activity.Stopwatch;
FSample Sample = {
ToMs(Stopwatch.GetInterval(0)),
ToMs(Stopwatch.GetInterval(1)),
ToMs(Stopwatch.GetInterval(2)),
};
uint32 Bps = ~0u;
if (Sample.RecvMs)
{
Bps = uint32((Activity.Response.ContentLength * 1000ull) / Sample.RecvMs);
}
Sample.RecvKiBps = Clamp(Bps >> 10);
return Sample;
}
#endif // IAS_HTTP_WITH_PERF
// }}}
} // namespace UE::IoStore::HTTP