363 lines
9.3 KiB
C++
363 lines
9.3 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
namespace UE::IoStore::HTTP
|
|
{
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
class FRequestCloak
|
|
: public FBuffer
|
|
{
|
|
public:
|
|
using FBuffer::FBuffer;
|
|
|
|
void Begin(FAnsiStringView Method, FAnsiStringView Path);
|
|
void AddHeader(FAnsiStringView Key, FAnsiStringView Value);
|
|
void End();
|
|
FAnsiStringView GetMethod() const;
|
|
void EnumerateHeaders(FResponse::FHeaderSink Sink) const;
|
|
|
|
private:
|
|
FRequestCloak& operator << (FAnsiStringView Value);
|
|
uint16 HeaderLeft;
|
|
int16 HeaderRight = -1;
|
|
int8 MethodLength = -1;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
FAnsiStringView FRequestCloak::GetMethod() const
|
|
{
|
|
check(MethodLength > 0);
|
|
return FAnsiStringView(GetData(), MethodLength);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void FRequestCloak::EnumerateHeaders(FResponse::FHeaderSink Sink) const
|
|
{
|
|
check(HeaderRight >= 2);
|
|
int32 HeaderLength = HeaderRight - HeaderLeft;
|
|
FAnsiStringView Headers(GetData() + HeaderLeft, HeaderLength);
|
|
UE::IoStore::HTTP::EnumerateHeaders(Headers, Sink);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void FRequestCloak::Begin(FAnsiStringView Method, FAnsiStringView Path)
|
|
{
|
|
check(MethodLength < 0);
|
|
*this << Method << " " << Path << " HTTP/1.1" "\r\n";
|
|
MethodLength = int8(Method.Len());
|
|
HeaderLeft = uint16(GetSize());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void FRequestCloak::AddHeader(FAnsiStringView Key, FAnsiStringView Value)
|
|
{
|
|
*this << Key << ":" << Value << "\r\n";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void FRequestCloak::End()
|
|
{
|
|
*this << "\r\n";
|
|
HeaderRight = int16(GetSize());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
FRequestCloak& FRequestCloak::operator << (FAnsiStringView Value)
|
|
{
|
|
uint32 Length = uint32(Value.Len());
|
|
FBuffer::FMutableSection Section = GetMutableFree(Length);
|
|
::memcpy(Section.Data, Value.GetData(), Length);
|
|
AdvanceUsed(Length);
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
#if IAS_HTTP_WITH_PERF
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
class FStopwatch
|
|
{
|
|
public:
|
|
uint64 GetInterval(uint32 i) const;
|
|
void SendStart() { Impl(0); }
|
|
void SendEnd() { Impl(1); }
|
|
void RecvStart() { Impl(2); }
|
|
void RecvEnd() { Impl(3); }
|
|
|
|
private:
|
|
void Impl(uint32 Index);
|
|
uint64 Samples[4] = {};
|
|
uint32 Counts[2] = {};
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
uint64 FStopwatch::GetInterval(uint32 i) const
|
|
{
|
|
if (i >= UE_ARRAY_COUNT(Samples) - 1)
|
|
{
|
|
return 0;
|
|
}
|
|
return Samples[i + 1] - Samples[i];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void FStopwatch::Impl(uint32 Index)
|
|
{
|
|
if (uint64& Out = Samples[Index]; Out == 0)
|
|
{
|
|
Out = FPlatformTime::Cycles64();
|
|
}
|
|
Counts[Index >> 1] += !(Index & 1);
|
|
}
|
|
|
|
#endif // IAS_HTTP_WITH_PERF
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static FLaneEstate* GActivityTraceEstate = LaneEstate_New({
|
|
.Name = "Iax/Activity",
|
|
.Group = "Iax",
|
|
.Channel = GetIaxTraceChannel(),
|
|
.Weight = 11,
|
|
});
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
struct FResponseInternal
|
|
{
|
|
FMessageOffsets Offsets;
|
|
int32 ContentLength = 0;
|
|
uint16 MessageLength;
|
|
mutable int16 Code;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
struct alignas(16) FActivity
|
|
{
|
|
enum class EState : uint8
|
|
{
|
|
None,
|
|
Build,
|
|
Send,
|
|
RecvMessage,
|
|
RecvStream,
|
|
RecvContent,
|
|
RecvDone,
|
|
Completed,
|
|
Cancelled,
|
|
Failed,
|
|
_Num,
|
|
};
|
|
|
|
FActivity* Next = nullptr;
|
|
int8 Slot = -1;
|
|
EState State = EState::None;
|
|
uint8 IsKeepAlive : 1;
|
|
uint8 NoContent : 1;
|
|
uint8 bFollow30x : 1;
|
|
uint8 bAllowChunked : 1;
|
|
uint8 LengthScore : 3;
|
|
uint8 _Unused : 1;
|
|
uint32 StateParam = 0;
|
|
#if IAS_HTTP_WITH_PERF
|
|
FStopwatch Stopwatch;
|
|
#endif
|
|
union {
|
|
FHost* Host;
|
|
FIoBuffer* Dest;
|
|
const char* ErrorReason;
|
|
};
|
|
UPTRINT SinkParam;
|
|
FTicketSink Sink;
|
|
FResponseInternal Response;
|
|
FRequestCloak Buffer;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static void Activity_ChangeState(FActivity* Activity, FActivity::EState InState, uint32 Param=0)
|
|
{
|
|
Trace(Activity, ETrace::StateChange, uint32(InState));
|
|
|
|
check(Activity->State != InState);
|
|
Activity->State = InState;
|
|
Activity->StateParam = Param;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static int32 Activity_Rewind(FActivity* Activity)
|
|
{
|
|
using EState = FActivity::EState;
|
|
|
|
if (Activity->State == EState::Send)
|
|
{
|
|
Activity->StateParam = 0;
|
|
return 0;
|
|
}
|
|
|
|
if (Activity->State == EState::RecvMessage)
|
|
{
|
|
Activity->Buffer.Resize(Activity->StateParam);
|
|
Activity_ChangeState(Activity, EState::Send);
|
|
return 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static uint32 Activity_RemainingKiB(FActivity* Activity)
|
|
{
|
|
if (Activity->State <= FActivity::EState::RecvStream) return MAX_uint32;
|
|
if (Activity->State > FActivity::EState::RecvContent) return 0;
|
|
|
|
uint32 ContentLength = uint32(Activity->Response.ContentLength);
|
|
check(Activity->StateParam <= ContentLength);
|
|
return (ContentLength - Activity->StateParam) >> 10;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static void Activity_CallSink(FActivity* Activity)
|
|
{
|
|
static uint32 Scope = LaneTrace_NewScope("Iax/Sink");
|
|
FLaneTrace* Lane = LaneEstate_Lookup(GActivityTraceEstate, Activity);
|
|
FLaneTraceScope _(Lane, Scope);
|
|
|
|
FTicketStatus& SinkArg = *(FTicketStatus*)Activity;
|
|
Activity->Sink(SinkArg);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static FActivity* Activity_Alloc(uint32 BufferSize)
|
|
{
|
|
BufferSize = (BufferSize + 15) & ~15;
|
|
|
|
uint32 Size = BufferSize + sizeof(FActivity);
|
|
auto* Activity = (FActivity*)FMemory::Malloc(Size, alignof(FActivity));
|
|
|
|
new (Activity) FActivity();
|
|
|
|
auto* Scratch = (char*)(Activity + 1);
|
|
uint32 ScratchSize = BufferSize;
|
|
Activity->Buffer = decltype(Activity->Buffer)(Scratch, ScratchSize);
|
|
|
|
return Activity;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static void Activity_Free(FActivity* Activity)
|
|
{
|
|
Trace(Activity, ETrace::ActivityDestroy, 0);
|
|
|
|
Activity->~FActivity();
|
|
FMemory::Free(Activity);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static void Activity_SetError(FActivity* Activity, const char* Reason, int32 Code=-1)
|
|
{
|
|
Activity->IsKeepAlive = 0;
|
|
Activity->ErrorReason = Reason;
|
|
|
|
Code = (Code < 0) ? LastSocketResult() : Code;
|
|
Activity_ChangeState(Activity, FActivity::EState::Failed, Code);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static void Activity_SetError(FActivity* Activity, const FOutcome& Outcome)
|
|
{
|
|
Activity_SetError(Activity, Outcome.GetMessage().GetData(), Outcome.GetErrorCode());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static void Activity_SetScore(FActivity* Activity, uint32 ContentSizeEst)
|
|
{
|
|
if (ContentSizeEst == 0)
|
|
{
|
|
Activity->LengthScore = 0;
|
|
return;
|
|
}
|
|
|
|
uint32 ContentEstKiB = (ContentSizeEst + 1023) >> 10;
|
|
ContentEstKiB |= 2;
|
|
uint32 Pow2 = FMath::FloorLog2(uint32(ContentEstKiB));
|
|
Pow2 = FMath::Min(Pow2, 7u);
|
|
Activity->LengthScore = uint8(Pow2);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
static void Trace(const struct FActivity* Activity, ETrace Action, uint32 Param)
|
|
{
|
|
if (Action == ETrace::ActivityCreate)
|
|
{
|
|
static uint32 ActScopes[8] = {};
|
|
if (ActScopes[0] == 0)
|
|
{
|
|
ActScopes[0] = LaneTrace_NewScope("Iax/Activity");
|
|
ActScopes[1] = LaneTrace_NewScope("Iax/Activity_2");
|
|
ActScopes[2] = LaneTrace_NewScope("Iax/Activity_4");
|
|
ActScopes[3] = LaneTrace_NewScope("Iax/Activity_8");
|
|
ActScopes[4] = LaneTrace_NewScope("Iax/Activity_16");
|
|
ActScopes[5] = LaneTrace_NewScope("Iax/Activity_32");
|
|
ActScopes[6] = LaneTrace_NewScope("Iax/Activity_128");
|
|
ActScopes[7] = LaneTrace_NewScope("Iax/Activity_256");
|
|
}
|
|
|
|
FLaneTrace* Lane = LaneEstate_Build(GActivityTraceEstate, Activity);
|
|
LaneTrace_Enter(Lane, ActScopes[Activity->LengthScore]);
|
|
return;
|
|
}
|
|
|
|
if (Action == ETrace::ActivityDestroy)
|
|
{
|
|
LaneEstate_Demolish(GActivityTraceEstate, Activity);
|
|
return;
|
|
}
|
|
|
|
FLaneTrace* Lane = LaneEstate_Lookup(GActivityTraceEstate, Activity);
|
|
|
|
if (Action == ETrace::StateChange)
|
|
{
|
|
static constexpr FAnsiStringView StateNames[] = {
|
|
"Iax/None",
|
|
"Iax/Build",
|
|
"Iax/WaitForSocket",
|
|
"Iax/WaitResponse",
|
|
"Iax/RecvStream",
|
|
"Iax/RecvContent",
|
|
"Iax/RecvDone",
|
|
"Iax/Completed",
|
|
"Iax/Cancelled",
|
|
"Iax/Failed",
|
|
};
|
|
static_assert(UE_ARRAY_COUNT(StateNames) == uint32(FActivity::EState::_Num));
|
|
static uint32 StateScopes[UE_ARRAY_COUNT(StateNames)] = {};
|
|
if (StateScopes[0] == 0)
|
|
{
|
|
for (int32 i = 0; FAnsiStringView Name : StateNames)
|
|
{
|
|
StateScopes[i++] = LaneTrace_NewScope(Name);
|
|
}
|
|
}
|
|
|
|
uint32 Scope = StateScopes[Param];
|
|
if (Param == uint32(FActivity::EState::Build))
|
|
{
|
|
LaneTrace_Enter(Lane, Scope);
|
|
}
|
|
else
|
|
{
|
|
LaneTrace_Change(Lane, Scope);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
|
|
} // namespace UE::IoStore::HTTP
|