Files
UnrealEngine/Engine/Plugins/Experimental/QuicMessaging/Source/QuicMessagingTransport/Private/QuicServer.cpp
2025-05-18 13:04:45 +08:00

1175 lines
28 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "QuicServer.h"
#include "QuicEndpointManager.h"
void FQuicServer::StartEndpoint()
{
QUIC_STATUS Status;
const QUIC_ADDR Address = GetAddressFromEndpoint(Endpoint);
/**
* Allocate new listener object
*/
if (QUIC_FAILED(Status = MsQuic->ListenerOpen(Registration,
ServerListenerCallback, this, &ServerListener)))
{
const EQuicEndpointError ListenerError = QuicUtils::ConvertQuicStatus(Status);
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Could not open listener: %s."),
*QuicUtils::GetEndpointErrorString(ListenerError));
return;
}
/**
* Start listening for incoming connections
*/
if (QUIC_FAILED(Status = MsQuic->ListenerStart(ServerListener, &Alpn, 1, &Address)))
{
const EQuicEndpointError ListenerError = QuicUtils::ConvertQuicStatus(Status);
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Could not start listener: %s."),
*QuicUtils::GetEndpointErrorString(ListenerError));
return;
}
ConnectionState = EQuicServerState::Running;
// Setup the connection cooldown check tick
ConnectionCooldownCheckHandle = FTSTicker::GetCoreTicker().AddTicker(
TEXT("QuicServerConnCooldownTick"), 1, [this](float DeltaSeconds) {
CheckConnectionCooldown();
return true;
});
UE_LOG(LogQuicMessagingTransport, Display,
TEXT("[QuicServer] Started server. Listening on %s."),
*GetEndpointFromAddress(Address).ToString());
}
void FQuicServer::StopEndpoint()
{
if (IsStopping())
{
return;
}
UE_LOG(LogQuicMessagingTransport, Verbose,
TEXT("[QuicServer] Stopping server ..."));
Stop();
if (ConnectionCooldownCheckHandle.IsValid())
{
FTSTicker::GetCoreTicker().RemoveTicker(ConnectionCooldownCheckHandle);
}
if (ConnectionState < EQuicServerState::Failed)
{
ConnectionState = EQuicServerState::Stopped;
}
if (MsQuic)
{
if (ServerListener)
{
MsQuic->ListenerStop(ServerListener);
MsQuic->ListenerClose(ServerListener);
}
ShutdownAllHandlers();
}
UE_LOG(LogQuicMessagingTransport, Verbose,
TEXT("[QuicServer] Stopped server."));
}
bool FQuicServer::IsEndpointReady()
{
return ConnectionState == EQuicServerState::Running;
}
void FQuicServer::UpdateEndpoint()
{
if (IsStopping() || ConnectionState != EQuicServerState::Running)
{
return;
}
if (Handlers.IsEmpty())
{
return;
}
ConsumeMessages();
}
void FQuicServer::ConsumeInboundMessages()
{
if (InboundMessages.IsEmpty())
{
return;
}
double CurrentTime = FPlatformTime::Seconds();
const double ConsumeLimit = CurrentTime + MaxConsumeTime.GetTotalSeconds();
FInboundMessage InboundMessage;
while (CurrentTime < ConsumeLimit && InboundMessages.Dequeue(InboundMessage))
{
MessageInboundDelegate.ExecuteIfBound(InboundMessage);
CurrentTime = FPlatformTime::Seconds();
}
}
void FQuicServer::ProcessInboundBuffers()
{
if (InboundQuicBuffers.IsEmpty())
{
return;
}
double CurrentTime = FPlatformTime::Seconds();
const double ConsumeLimit = CurrentTime + MaxConsumeTime.GetTotalSeconds();
FInboundQuicBuffer InboundBuffer;
while (CurrentTime < ConsumeLimit && InboundQuicBuffers.Dequeue(InboundBuffer))
{
if (ReceiveFromStream(InboundBuffer))
{
if (InboundBuffer.bEndOfStream)
{
ReachedEndOfStream(InboundBuffer);
}
}
CurrentTime = FPlatformTime::Seconds();
}
}
void FQuicServer::ConsumeOutboundMessages()
{
if (OutboundMessages.IsEmpty())
{
return;
}
double CurrentTime = FPlatformTime::Seconds();
const double ConsumeLimit = CurrentTime + MaxConsumeTime.GetTotalSeconds();
FOutboundMessage OutboundMessage;
while (CurrentTime < ConsumeLimit && OutboundMessages.Dequeue(OutboundMessage))
{
HQUIC HandlerConnection;
{
FScopeLock HandlersLock(&HandlersCS);
const FQuicServerHandlerPtr* ServerHandler
= Handlers.Find(OutboundMessage.Recipient);
if (!ServerHandler)
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Could not find server handler when preparing outbound message: %s"),
*OutboundMessage.Recipient.ToString());
continue;
}
if ((*ServerHandler)->IsStopping())
{
continue;
}
if (!(*ServerHandler)->IsRunning())
{
OutboundMessages.Enqueue(OutboundMessage);
continue;
}
HandlerConnection = (*ServerHandler)->Connection;
}
SendToStream(HandlerConnection, OutboundMessage);
CurrentTime = FPlatformTime::Seconds();
}
}
bool FQuicServer::CanHandlerConnect(const FIPv4Endpoint& ConnectingEndpoint) const
{
const FIPv4Endpoint ClearedEndpoint = ClearEndpointPort(ConnectingEndpoint);
FScopeLock ConnCooldownLock(&ConnectionCooldownCS);
return !ConnectionCooldown.Contains(ClearedEndpoint);
}
void FQuicServer::AddConnectionAttempt(const FIPv4Endpoint& ConnectingEndpoint)
{
if (ServerConfig->ConnCooldownMode != EConnectionCooldownMode::Enabled)
{
return;
}
const FIPv4Endpoint ClearedEndpoint = ClearEndpointPort(ConnectingEndpoint);
FScopeLock ConnCooldownLock(&ConnectionCooldownCS);
const uint64 Now = FMath::Floor(FPlatformTime::Seconds());
FConnectionAttempts* ExistingAttempts = ConnectionAttempts.Find(ClearedEndpoint);
if (!ExistingAttempts)
{
FConnectionAttempts Attempts;
Attempts.AttemptTimestamps.Add(Now);
ConnectionAttempts.Add(ClearedEndpoint, Attempts);
return;
}
ExistingAttempts->AttemptTimestamps.Add(Now);
const int32 MaximumAttempts = ServerConfig->ConnCooldownMaxAttempts;
// Prune unnecessary previous attempts
if (ExistingAttempts->AttemptTimestamps.Num() > MaximumAttempts)
{
const int32 RemoveNumAttempts
= FMath::Abs(MaximumAttempts - ExistingAttempts->AttemptTimestamps.Num());
ExistingAttempts->AttemptTimestamps.RemoveAt(0, RemoveNumAttempts);
}
// Not enough attempts
if (ExistingAttempts->AttemptTimestamps.Num() < MaximumAttempts)
{
return;
}
const uint64 EarliestAttemptTimestamp = ExistingAttempts->AttemptTimestamps[0];
// Earliest attempt is longer ago
if (Now > (EarliestAttemptTimestamp + ServerConfig->ConnCooldownPeriodSec))
{
return;
}
// Calculate cooldown multiplier
const uint64 CooldownMultiplier = FMath::Floor(
FMath::Exp2(static_cast<float>(ExistingAttempts->NumCooldowns)));
// Get the cooldown seconds
uint64 CooldownSeconds = ServerConfig->ConnCooldownSec * CooldownMultiplier;
// Make sure it doesn't exceed the maximum cooldown
CooldownSeconds = FMath::Min(CooldownSeconds,
static_cast<uint64>(ServerConfig->ConnCooldownMaxSec));
const uint64 CooldownExpires = Now + (CooldownSeconds * CooldownMultiplier);
ExistingAttempts->NumCooldowns++;
ExistingAttempts->AttemptTimestamps.Empty();
ConnectionCooldown.Add(ClearedEndpoint, CooldownExpires);
UE_LOG(LogQuicMessagingTransport, Warning,
TEXT("Connection cooldown of %d seconds for IP %s."),
CooldownSeconds, *ClearedEndpoint.ToInternetAddr()->ToString(false));
}
void FQuicServer::CheckConnectionCooldown()
{
FScopeLock ConnCooldownLock(&ConnectionCooldownCS);
if (ConnectionCooldown.IsEmpty())
{
return;
}
if (ServerConfig->ConnCooldownMode == EConnectionCooldownMode::Disabled)
{
ConnectionCooldown.Empty();
return;
}
const uint64 Now = FMath::Floor(FPlatformTime::Seconds());
for (auto CooldownIt = ConnectionCooldown.CreateIterator(); CooldownIt; ++CooldownIt)
{
if (Now > CooldownIt->Value)
{
CooldownIt.RemoveCurrent();
}
}
}
/*
* TODO(vri): Fully implement and fix server statistics
* Tracked in [UCS-5152] Finalize QuicMessaging plugin
*/
void FQuicServer::CollectStatistics()
{
if (IsStopping() || ConnectionState != EQuicServerState::Running)
{
return;
}
if (MsQuic == nullptr)
{
return;
}
FScopeLock HandlersLock(&HandlersCS);
TArray<FIPv4Endpoint> UpdateHandlers;
Handlers.GetKeys(UpdateHandlers);
for (FIPv4Endpoint& UpdateEndpoint : UpdateHandlers)
{
FQuicServerHandlerPtr Handler = Handlers[UpdateEndpoint];
if (!Handler.IsValid() || !Handler->IsRunning())
{
continue;
}
QUIC_STATISTICS Stats{};
const QUIC_STATUS Status = GetConnectionStats(Handler->Connection, Stats);
if (Status != QUIC_STATUS_SUCCESS)
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Could not collect statistics: %s."),
*QuicUtils::ConvertResult(Status));
return;
}
if (Handler->NeedRttReset())
{
Handler->RttMeasurements.Empty();
Handler->RttStart = FPlatformTime::Seconds();
}
Handler->RttMeasurements.Add(Stats.Rtt);
FMessageTransportStatistics TransportStats = ConvertStatistics(Handler->Endpoint, Stats);
TransportStats.AverageRTT = FTimespan::FromMicroseconds(Handler->GetAverageRtt());
StatisticsUpdatedDelegate.ExecuteIfBound(TransportStats);
}
}
void FQuicServer::SetHandlerAuthenticated(const FIPv4Endpoint& HandlerEndpoint)
{
FScopeLock HandlersLock(&HandlersCS);
if (!Handlers.Contains(HandlerEndpoint))
{
return;
}
Handlers[HandlerEndpoint]->bAuthenticated = true;
}
void FQuicServer::DisconnectHandler(const FIPv4Endpoint& HandlerEndpoint)
{
FScopeLock HandlersLock(&HandlersCS);
const FQuicServerHandlerPtr* Handler = Handlers.Find(HandlerEndpoint);
if (!Handler)
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Could not find handler to disconnect: %s."),
*HandlerEndpoint.ToString());
return;
}
ShutdownHandler((*Handler)->Connection);
}
void FQuicServer::ShutdownAllHandlers() const
{
FScopeLock HandlersLock(&HandlersCS);
for (const auto& Handler : Handlers)
{
if (Handler.Value->Connection)
{
ShutdownHandler(Handler.Value->Connection);
}
}
}
void FQuicServer::ShutdownHandler(const HQUIC HandlerConnection) const
{
if (MsQuic)
{
MsQuic->ConnectionShutdown(
HandlerConnection, QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0);
}
}
void FQuicServer::EnqueueOutboundMessage(const FOutboundMessage& OutboundMessage)
{
OutboundMessages.Enqueue(OutboundMessage);
}
void FQuicServer::SendHello(FIPv4Endpoint RemoteEndpoint)
{
if (IsStopping() || ConnectionState != EQuicServerState::Running)
{
return;
}
FMessageHeader MessageHeader(EQuicMessageType::Hello, FGuid::NewGuid(), LocalNodeId, 0);
const FQuicPayloadPtr MetaBuffer = SerializeHeaderDelegate.Execute(MessageHeader);
const FOutboundMessage OutboundMessage(RemoteEndpoint, MetaBuffer);
EnqueueOutboundMessage(OutboundMessage);
UE_LOG(LogQuicMessagingTransport, Verbose,
TEXT("[QuicServer] Sent hello to handler %s."), *RemoteEndpoint.ToString());
}
void FQuicServer::SendBye()
{
if (IsStopping() || ConnectionState != EQuicServerState::Running)
{
return;
}
TArray<FIPv4Endpoint> HandlerEndpoints;
Handlers.GenerateKeyArray(HandlerEndpoints);
for (FIPv4Endpoint& HandlerEndpoint : HandlerEndpoints)
{
SendBye(HandlerEndpoint);
}
}
void FQuicServer::SendBye(const FIPv4Endpoint& HandlerEndpoint)
{
if (IsStopping() || ConnectionState != EQuicServerState::Running)
{
return;
}
FMessageHeader MessageHeader(EQuicMessageType::Bye, FGuid(), LocalNodeId, 0);
const FQuicPayloadPtr MetaBuffer = SerializeHeaderDelegate.Execute(MessageHeader);
const FOutboundMessage OutboundMessage(HandlerEndpoint, MetaBuffer);
HQUIC HandlerConnection;
{
FScopeLock HandlersLock(&HandlersCS);
const FQuicServerHandlerPtr* ServerHandler
= Handlers.Find(OutboundMessage.Recipient);
if (!ServerHandler)
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Could not find server handler when sending bye message: %s"),
*OutboundMessage.Recipient.ToString());
return;
}
HandlerConnection = (*ServerHandler)->Connection;
}
SendToStream(HandlerConnection, OutboundMessage);
UE_LOG(LogQuicMessagingTransport, Verbose,
TEXT("[QuicServer] Sent bye to handler %s."), *HandlerEndpoint.ToString());
}
_Function_class_(QUIC_LISTENER_CALLBACK)
QUIC_STATUS FQuicServer::ServerListenerCallback(HQUIC Listener,
void* Context, QUIC_LISTENER_EVENT* Event)
{
UNREFERENCED_PARAMETER(Listener);
FQuicServer* ServerContext = static_cast<FQuicServer*>(Context);
QUIC_STATUS Status = QUIC_STATUS_NOT_SUPPORTED;
/**
* Connection attempt by a client
*/
if (Event->Type == QUIC_LISTENER_EVENT_NEW_CONNECTION)
{
Status = ServerContext->OnListenerNewConnection(Event);
}
return Status;
}
_Function_class_(QUIC_CONNECTION_CALLBACK)
QUIC_STATUS FQuicServer::ServerConnectionCallback(HQUIC Connection,
void* Context, QUIC_CONNECTION_EVENT* Event)
{
FQuicServer* ServerContext = static_cast<FQuicServer*>(Context);
switch (Event->Type)
{
case QUIC_CONNECTION_EVENT_CONNECTED:
/**
* Connection handshake has completed
*/
ServerContext->OnConnectionConnected(Connection);
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT:
/**
* Idle timeout killed the connection
*/
ServerContext->OnConnectionShutdownByTransport(Connection, Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status);
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER:
/**
* Peer killed the connection
*/
ServerContext->OnConnectionShutdownByPeer(Connection, Event->SHUTDOWN_INITIATED_BY_PEER.ErrorCode);
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE:
/**
* Connection shutdown complete, ready to cleanup
*/
ServerContext->OnConnectionShutdownComplete(Connection, Event->SHUTDOWN_COMPLETE.AppCloseInProgress);
break;
case QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED:
{
/**
* Peer started a stream, set local stream callback handler
*/
const QUIC_ADDR HandlerAddress = ServerContext->GetRemoteAddress(Connection);
FIPv4Endpoint* PeerAddress = new FIPv4Endpoint(
ServerContext->GetEndpointFromAddress(HandlerAddress));
FStreamCallbackInfo* StreamCallbackInfo = new FStreamCallbackInfo();
StreamCallbackInfo->Endpoint = ServerContext;
StreamCallbackInfo->PeerAddress = PeerAddress;
ServerContext->OnConnectionPeerStreamStarted(Connection, Event->PEER_STREAM_STARTED.Stream);
ServerContext->MsQuic->SetCallbackHandler(Event->PEER_STREAM_STARTED.Stream,
reinterpret_cast<void*>(ServerStreamCallback), StreamCallbackInfo);
break;
}
case QUIC_CONNECTION_EVENT_RESUMED:
/**
* TLS resumption of previous session succeeded
*/
ServerContext->OnConnectionResumed();
case QUIC_CONNECTION_EVENT_DATAGRAM_RECEIVED:
case QUIC_CONNECTION_EVENT_DATAGRAM_SEND_STATE_CHANGED:
case QUIC_CONNECTION_EVENT_DATAGRAM_STATE_CHANGED:
case QUIC_CONNECTION_EVENT_IDEAL_PROCESSOR_CHANGED:
case QUIC_CONNECTION_EVENT_LOCAL_ADDRESS_CHANGED:
case QUIC_CONNECTION_EVENT_PEER_ADDRESS_CHANGED:
case QUIC_CONNECTION_EVENT_PEER_CERTIFICATE_RECEIVED:
case QUIC_CONNECTION_EVENT_PEER_NEEDS_STREAMS:
case QUIC_CONNECTION_EVENT_RESUMPTION_TICKET_RECEIVED:
case QUIC_CONNECTION_EVENT_STREAMS_AVAILABLE:
break;
default:
UE_LOG(LogQuicMessagingTransport, Warning,
TEXT("[QuicServer] Received unhandled connection event: %d"), Event->Type);
break;
}
return QUIC_STATUS_SUCCESS;
}
_Function_class_(QUIC_STREAM_CALLBACK)
QUIC_STATUS FQuicServer::ServerStreamCallback(HQUIC Stream,
void* Context, QUIC_STREAM_EVENT* Event)
{
const FStreamCallbackInfo* CallbackInfo = static_cast<FStreamCallbackInfo*>(Context);
FQuicServer* ServerContext = static_cast<FQuicServer*>(CallbackInfo->Endpoint);
switch (Event->Type)
{
case QUIC_STREAM_EVENT_SEND_COMPLETE:
/**
* Previous StreamSend has completed, context returned
*/
ServerContext->OnStreamSendComplete(Stream, Event);
break;
case QUIC_STREAM_EVENT_RECEIVE:
/**
* Data received from the peer
*/
ServerContext->OnStreamReceive(Stream, *CallbackInfo->PeerAddress, Event);
return QUIC_STATUS_PENDING;
case QUIC_STREAM_EVENT_PEER_SEND_ABORTED:
/**
* Peer aborted its stream send direction
*/
ServerContext->OnStreamPeerSendAborted(Stream);
break;
case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN:
/**
* Peer gracefully shut down its stream send direction
*/
break;
case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE:
/**
* Both stream directions have been shut down, safe to cleanup
*/
ServerContext->OnStreamShutdownComplete(Stream);
delete CallbackInfo;
break;
case QUIC_STREAM_EVENT_START_COMPLETE:
case QUIC_STREAM_EVENT_PEER_ACCEPTED:
case QUIC_STREAM_EVENT_PEER_RECEIVE_ABORTED:
case QUIC_STREAM_EVENT_IDEAL_SEND_BUFFER_SIZE:
case QUIC_STREAM_EVENT_SEND_SHUTDOWN_COMPLETE:
break;
default:
UE_LOG(LogQuicMessagingTransport, Warning,
TEXT("[QuicServer] Received unhandled stream event: %d"), Event->Type);
break;
}
return QUIC_STATUS_SUCCESS;
}
QUIC_STATUS FQuicServer::OnListenerNewConnection(QUIC_LISTENER_EVENT* Event)
{
const HQUIC HandlerConnection = Event->NEW_CONNECTION.Connection;
const QUIC_ADDR HandlerAddress = GetRemoteAddress(Event->NEW_CONNECTION.Connection);
const FIPv4Endpoint HandlerEndpoint = GetEndpointFromAddress(HandlerAddress);
if (!CanHandlerConnect(HandlerEndpoint))
{
return QUIC_STATUS_ABORTED;
}
AddConnectionAttempt(HandlerEndpoint);
if (ServerConfig->EncryptionMode == EEncryptionMode::Disabled)
{
constexpr BOOLEAN Value = true;
MsQuic->SetParam(
Event->NEW_CONNECTION.Connection,
QUIC_PARAM_CONN_DISABLE_1RTT_ENCRYPTION,
sizeof(Value), &Value);
}
MsQuic->SetCallbackHandler(Event->NEW_CONNECTION.Connection,
reinterpret_cast<void*>(ServerConnectionCallback), this);
const QUIC_STATUS Status = MsQuic->ConnectionSetConfiguration(
Event->NEW_CONNECTION.Connection, QuicConfig);
{
FScopeLock HandlersLock(&HandlersCS);
const TSharedPtr<FQuicServerHandler, ESPMode::ThreadSafe> Handler
= MakeShared<FQuicServerHandler, ESPMode::ThreadSafe>(
HandlerEndpoint, HandlerAddress, HandlerConnection);
Handlers.Add(HandlerEndpoint, Handler);
}
UE_LOG(LogQuicMessagingTransport, Verbose,
TEXT("[QuicServer] Connection attempt by client: %s."),
*HandlerEndpoint.ToString());
return Status;
}
void FQuicServer::OnConnectionConnected(HQUIC Connection)
{
const QUIC_ADDR Handler = GetRemoteAddress(Connection);
const FIPv4Endpoint HandlerEndpoint = GetEndpointFromAddress(Handler);
{
FScopeLock HandlersLock(&HandlersCS);
if (const FQuicServerHandlerPtr* ServerHandler = Handlers.Find(HandlerEndpoint))
{
(*ServerHandler)->ConnectionState = EQuicHandlerState::Running;
(*ServerHandler)->bSentHello = true;
}
}
SendHello(HandlerEndpoint);
EndpointConnectedDelegate.ExecuteIfBound(HandlerEndpoint);
UE_LOG(LogQuicMessagingTransport, Verbose,
TEXT("[QuicServer] Handshake completed. Handler: %s."), *HandlerEndpoint.ToString());
}
void FQuicServer::OnConnectionShutdownByTransport(HQUIC Connection, QuicUtils::HRESULT Status)
{
const QUIC_ADDR Handler = GetRemoteAddress(Connection);
const FIPv4Endpoint HandlerEndpoint = GetEndpointFromAddress(Handler);
const EQuicEndpointError HandlerError = QuicUtils::ConvertQuicStatus(Status);
{
FScopeLock HandlersLock(&HandlersCS);
if (const FQuicServerHandlerPtr* ServerHandler = Handlers.Find(HandlerEndpoint))
{
(*ServerHandler)->ConnectionState = EQuicHandlerState::Disconnected;
(*ServerHandler)->Error = HandlerError;
}
}
UE_LOG(LogQuicMessagingTransport, Verbose,
TEXT("[QuicServer] Connection end (by transport). Handler: %s."),
*HandlerEndpoint.ToString());
if (HandlerError != EQuicEndpointError::Normal)
{
UE_LOG(LogQuicMessagingTransport, Verbose,
TEXT("[QuicServer] Handler: %s / shutdown reason: %s."),
*HandlerEndpoint.ToString(), *QuicUtils::GetEndpointErrorString(HandlerError));
}
}
void FQuicServer::OnConnectionShutdownByPeer(HQUIC Connection, QUIC_UINT62 ErrorCode)
{
const QUIC_ADDR Handler = GetRemoteAddress(Connection);
const FIPv4Endpoint HandlerEndpoint = GetEndpointFromAddress(Handler);
{
FScopeLock HandlersLock(&HandlersCS);
if (const FQuicServerHandlerPtr* ServerHandler = Handlers.Find(HandlerEndpoint))
{
(*ServerHandler)->ConnectionState = EQuicHandlerState::Disconnected;
}
}
UE_LOG(LogQuicMessagingTransport, Verbose,
TEXT("[QuicServer] Connection end (by peer). Handler: %s / Error code: %d."),
*HandlerEndpoint.ToString(), ErrorCode);
}
void FQuicServer::OnConnectionShutdownComplete(HQUIC Connection, BOOLEAN AppCloseInProgress)
{
FScopeLock HandlersLock(&HandlersCS);
const QUIC_ADDR Handler = GetRemoteAddress(Connection);
const FIPv4Endpoint HandlerEndpoint = GetEndpointFromAddress(Handler);
if (!AppCloseInProgress)
{
Handlers.Remove(GetEndpointFromAddress(Handler));
HandlersLock.Unlock();
EndpointDisconnectedDelegate.ExecuteIfBound(
HandlerEndpoint, EQuicEndpointError::Normal);
if (MsQuic)
{
MsQuic->ConnectionClose(Connection);
}
}
UE_LOG(LogQuicMessagingTransport, Verbose,
TEXT("[QuicServer] Connection shutdown complete. Handler: %s."),
*HandlerEndpoint.ToString());
}
void FQuicServer::OnConnectionPeerStreamStarted(HQUIC Connection, HQUIC Stream)
{
const uint64 StreamId = GetStreamId(Stream);
const QUIC_ADDR HandlerAddress = GetRemoteAddress(Connection);
const FIPv4Endpoint HandlerEndpoint = GetEndpointFromAddress(HandlerAddress);
{
const FQuicPayloadPtr InboundData = MakeShared<TArray<uint8>, ESPMode::ThreadSafe>();
FScopeLock InboundLock(&InboundBuffersCS);
const FInboundBuffer InboundBuffer(HandlerAddress, HandlerEndpoint, InboundData);
const FAddrStream AddressStreamKey(HandlerEndpoint, StreamId);
InboundBuffers.Add(AddressStreamKey, InboundBuffer);
}
UE_LOG(LogQuicMessagingTransport, VeryVerbose,
TEXT("[QuicServer] Peer stream started. Handler: %s / StreamId: %d."),
*HandlerEndpoint.ToString(), StreamId);
}
void FQuicServer::OnConnectionResumed()
{
UE_LOG(LogQuicMessagingTransport, Verbose,
TEXT("[QuicServer] Connection resumed."));
}
void FQuicServer::OnStreamShutdownComplete(HQUIC Stream) const
{
if (MsQuic)
{
MsQuic->StreamClose(Stream);
}
}
void FQuicServer::SendToStream(
const HQUIC Connection, const FOutboundMessage OutboundMessage)
{
HQUIC Stream = nullptr;
QUIC_STATUS Status;
FIPv4Endpoint* PeerAddress = new FIPv4Endpoint(OutboundMessage.Recipient);
FStreamCallbackInfo* StreamCallbackInfo = new FStreamCallbackInfo();
StreamCallbackInfo->Endpoint = this;
StreamCallbackInfo->PeerAddress = PeerAddress;
/**
* Create/allocate a new unidirectional stream
*/
if (QUIC_FAILED(Status = MsQuic->StreamOpen(Connection, QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL,
ServerStreamCallback, StreamCallbackInfo, &Stream)))
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Could not open stream: %d."), QuicUtils::ConvertQuicStatus(Status));
return;
}
/**
* Start the stream
*/
if (QUIC_FAILED(Status = MsQuic->StreamStart(Stream, QUIC_STREAM_START_FLAG_NONE)))
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Could not start stream: %d."), QuicUtils::ConvertQuicStatus(Status));
MsQuic->StreamClose(Stream);
return;
}
const uint64 StreamId = GetStreamId(Stream);
/**
* Send buffers over the stream
*/
const FQuicBufferRef MetaBuffer = MakeShared<QUIC_BUFFER, ESPMode::ThreadSafe>();
const FQuicBufferRef MessageBuffer = MakeShared<QUIC_BUFFER, ESPMode::ThreadSafe>();
MetaBuffer->Buffer = OutboundMessage.SerializedHeader->GetData();
MetaBuffer->Length = OutboundMessage.SerializedHeader->Num();
RegisterStreamMessage(StreamId, *PeerAddress,
!OutboundMessage.bHasPayload, OutboundMessage, MetaBuffer, MessageBuffer);
if (QUIC_FAILED(Status = MsQuic->StreamSend(Stream, &*MetaBuffer, 1,
OutboundMessage.bHasPayload ? QUIC_SEND_FLAG_NONE : QUIC_SEND_FLAG_FIN, PeerAddress)))
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Could not send meta on stream: %d."),
QuicUtils::ConvertQuicStatus(Status));
MsQuic->StreamShutdown(Stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0);
return;
}
// This message is header-only
if (!OutboundMessage.bHasPayload)
{
return;
}
MessageBuffer->Buffer = OutboundMessage.SerializedPayload->GetData();
MessageBuffer->Length = OutboundMessage.SerializedPayload->Num();
if (QUIC_FAILED(Status = MsQuic->StreamSend(Stream,
&*MessageBuffer, 1, QUIC_SEND_FLAG_FIN, PeerAddress)))
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Could not send message on stream: %d."),
QuicUtils::ConvertQuicStatus(Status));
MsQuic->StreamShutdown(Stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0);
}
}
bool FQuicServer::ReceiveFromStream(const FInboundQuicBuffer& QuicBuffer)
{
FScopeLock InboundLock(&InboundBuffersCS);
const FAddrStream AddressStreamKey(
QuicBuffer.PeerAddress, QuicBuffer.StreamId);
FInboundBuffer* Buffer = InboundBuffers.Find(AddressStreamKey);
if (!Buffer)
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Invalid inbound buffer in ReceiveFromStream %d."),
QuicBuffer.StreamId);
return false;
}
const FQuicPayloadPtr& InboundData = Buffer->InboundData;
for (const TPair<uint8*, uint32> InboundBuffer : QuicBuffer.Data)
{
Buffer->InboundData->Append(InboundBuffer.Key, InboundBuffer.Value);
}
// Retrieve message header
if (!Buffer->bHeaderDeserialized
&& InboundData->Num() >= QUIC_MESSAGE_HEADER_SIZE)
{
const FQuicPayloadPtr HeaderData = MakeShared<TArray<uint8>, ESPMode::ThreadSafe>();
HeaderData->Append(InboundData->GetData(), QUIC_MESSAGE_HEADER_SIZE);
const FMessageHeader MessageHeader = DeserializeHeaderDelegate.Execute(HeaderData);
Buffer->MessageHeader = MessageHeader;
Buffer->bHeaderDeserialized = true;
InboundData->RemoveAt(0, QUIC_MESSAGE_HEADER_SIZE);
const uint32& SerializedMessageSize = MessageHeader.SerializedMessageSize;
InboundData->Reserve(SerializedMessageSize);
// Handler is not authenticated and trying to send message with type Data
if (AuthenticationMode == EAuthenticationMode::Enabled
&& MessageHeader.MessageType == EQuicMessageType::Data)
{
{
const FIPv4Endpoint HandlerAddress
= GetEndpointFromAddress(Buffer->HandlerAddress);
FScopeLock HandlersLock(&HandlersCS);
const FQuicServerHandlerPtr* Handler = Handlers.Find(HandlerAddress);
if (!Handler)
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Cannot check authentication status of invalid handler %s."),
*HandlerAddress.ToString());
return false;
}
if (!(*Handler)->bAuthenticated)
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Unauthenticated handler sending data."));
return false;
}
}
}
}
// Check authentication message size
if (Buffer->bHeaderDeserialized)
{
if (Buffer->MessageHeader.MessageType == EQuicMessageType::Authentication
&& InboundData->Num() > static_cast<int32>(ServerConfig->MaxAuthenticationMessageSize))
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Authentication message size too large."));
return false;
}
}
InboundLock.Unlock();
if (MsQuic)
{
MsQuic->StreamReceiveComplete(QuicBuffer.Stream, QuicBuffer.BufferLength);
}
return true;
}
void FQuicServer::ReachedEndOfStream(const FInboundQuicBuffer& QuicBuffer)
{
FInboundBuffer InboundBuffer;
{
const FAddrStream AddressStreamKey(QuicBuffer.PeerAddress, QuicBuffer.StreamId);
FScopeLock InboundLock(&InboundBuffersCS);
if (!InboundBuffers.RemoveAndCopyValue(AddressStreamKey, InboundBuffer))
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Invalid inbound buffer in ReachedEndOfStream."));
return;
}
}
const FQuicPayloadPtr& InboundData = InboundBuffer.InboundData;
const FMessageHeader& MessageHeader = InboundBuffer.MessageHeader;
// Transmitted message size smaller than expected
if (InboundData->Num() < static_cast<int32>(MessageHeader.SerializedMessageSize))
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Transmitted message size smaller than expected %d / %d / %d."),
QuicBuffer.StreamId, InboundData->Num(), MessageHeader.SerializedMessageSize);
return;
}
FInboundMessage InboundMessage(InboundData, MessageHeader,
InboundBuffer.HandlerEndpoint, Endpoint);
InboundMessages.Enqueue(InboundMessage);
}
QUIC_ADDR FQuicServer::GetRemoteAddress(HQUIC Connection) const
{
QUIC_ADDR RemoteAddress = {{0}};
uint32_t RemoteAddressSize = sizeof(RemoteAddress);
if (MsQuic)
{
QUIC_STATUS Status;
if (QUIC_FAILED(Status = MsQuic->GetParam(Connection,
QUIC_PARAM_CONN_REMOTE_ADDRESS, &RemoteAddressSize, &RemoteAddress)))
{
UE_LOG(LogQuicMessagingTransport, Error,
TEXT("[QuicServer] Could not get QUIC address from connection: %s."),
*QuicUtils::ConvertResult(Status));
}
}
return RemoteAddress;
}
FIPv4Endpoint FQuicServer::ClearEndpointPort(const FIPv4Endpoint& Endpoint)
{
FIPv4Endpoint ClearedEndpoint = Endpoint;
ClearedEndpoint.Port = 0;
return ClearedEndpoint;
}