Files
UnrealEngine/Engine/Source/Runtime/Online/XMPP/Private/XmppStrophe/StropheWebsocketConnection.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

222 lines
6.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "XmppStrophe/StropheWebsocketConnection.h"
#include "XmppConnection.h"
#include "XmppLog.h"
#include "XmppStrophe/StropheContext.h"
#include "XmppStrophe/StropheError.h"
#include "XmppStrophe/StropheStanza.h"
#include "XmppStrophe/XmppConnectionStrophe.h"
#if WITH_XMPP_STROPHE
#include "WebSocketsModule.h"
#include "IWebSocket.h"
THIRD_PARTY_INCLUDES_START
#include "strophe.h"
THIRD_PARTY_INCLUDES_END
int StropheWebsocketStanzaEventHandler(xmpp_conn_t* const UnusedPtr, xmpp_stanza_t* const EventStanza, void* const VoidConnectionPtr)
{
check(VoidConnectionPtr != nullptr);
const FStropheStanza IncomingStanza(EventStanza);
// Ignore session stanza (bug in libstrophe that we see get this at all)
if (!IncomingStanza.GetId().Equals(TEXT("_xmpp_session1"), ESearchCase::CaseSensitive))
{
// We want to forward our stanza to our connection and out to our handlers
FXmppConnectionStrophe* const ConnectionPtr = static_cast<FXmppConnectionStrophe* const>(VoidConnectionPtr);
ConnectionPtr->ReceiveStanza(IncomingStanza);
}
return 1;
}
static void StropheWebsocketConnectionEventHandler(xmpp_conn_t* const UnusedPtr,
const xmpp_conn_event_t ConnectionEvent,
const int ErrorNo,
xmpp_stream_error_t* const StreamError,
void* const VoidConnectionPtr)
{
check(VoidConnectionPtr != nullptr);
FXmppConnectionStrophe* const ConnectionPtr = static_cast<FXmppConnectionStrophe* const>(VoidConnectionPtr);
EStropheConnectionEvent Event = EStropheConnectionEvent::Fail;
switch (ConnectionEvent)
{
case XMPP_CONN_CONNECT:
Event = EStropheConnectionEvent::Connect;
break;
case XMPP_CONN_RAW_CONNECT:
Event = EStropheConnectionEvent::RawConnect;
break;
case XMPP_CONN_DISCONNECT:
Event = EStropheConnectionEvent::Disconnect;
break;
case XMPP_CONN_FAIL:
Event = EStropheConnectionEvent::Fail;
break;
}
ConnectionPtr->ReceiveConnectionStateChange(Event);
if (StreamError != nullptr)
{
const FStropheError Error(*StreamError, ErrorNo);
ConnectionPtr->ReceiveConnectionError(Error, Event);
}
}
FStropheWebsocketConnection::FStropheWebsocketConnection(FStropheContext& InContext, const FString& Url)
: Context(InContext)
{
TArray<FString> Protocols;
Protocols.Add(TEXT("xmpp"));
Websocket = FWebSocketsModule::Get().CreateWebSocket(Url, Protocols);
Websocket->OnConnected().AddRaw(this, &FStropheWebsocketConnection::OnWebsocketConnected);
Websocket->OnConnectionError().AddRaw(this, &FStropheWebsocketConnection::OnWebsocketConnectionError);
Websocket->OnClosed().AddRaw(this, &FStropheWebsocketConnection::OnWebsocketClosed);
Websocket->OnRawMessage().AddRaw(this, &FStropheWebsocketConnection::OnRawMessage);
checkf(Context.GetContextPtr() != nullptr, TEXT("xmpp_ctx_t is null"));
XmppConnectionPtr = xmpp_conn_new(Context.GetContextPtr());
const int32 IsWebsocket = 1;
xmpp_conn_extsock_t ExternalSocket =
{
&FStropheWebsocketConnection::WebsocketConnectHandler,
&FStropheWebsocketConnection::WebsocketCloseHandler,
&FStropheWebsocketConnection::WebsocketSendHandler,
IsWebsocket,
this
};
xmpp_conn_set_extsock_handlers(XmppConnectionPtr, &ExternalSocket);
}
FStropheWebsocketConnection::~FStropheWebsocketConnection()
{
Websocket->OnConnected().RemoveAll(this);
Websocket->OnConnectionError().RemoveAll(this);
Websocket->OnClosed().RemoveAll(this);
Websocket->OnRawMessage().RemoveAll(this);
xmpp_conn_release(XmppConnectionPtr);
}
bool FStropheWebsocketConnection::Connect(const FXmppUserJid& UserJid, const FString& Password, FXmppConnectionStrophe& ConnectionManager)
{
bool bSuccess = true;
ConnectionManager.ProcessLoginStatusChange(EXmppLoginStatus::ProcessingLogin);
const FString UserJidFullPath = UserJid.GetFullPath();
xmpp_conn_set_jid(XmppConnectionPtr, TCHAR_TO_UTF8(*UserJidFullPath));
xmpp_conn_set_pass(XmppConnectionPtr, TCHAR_TO_UTF8(*Password));
const int Result = xmpp_extsock_connect_client(XmppConnectionPtr, StropheWebsocketConnectionEventHandler, &ConnectionManager);
if (Result != XMPP_EOK)
{
UE_LOG(LogXmpp, Error, TEXT("Websocket failed to connect. Error %i"), Result);
bSuccess = false;
ConnectionManager.ProcessLoginStatusChange(EXmppLoginStatus::LoggedOut);
}
xmpp_handler_add(XmppConnectionPtr, StropheWebsocketStanzaEventHandler, nullptr, nullptr, nullptr, &ConnectionManager);
return bSuccess;
}
bool FStropheWebsocketConnection::SendStanza(FStropheStanza&& Stanza)
{
if (!Websocket->IsConnected())
{
return false;
}
xmpp_send(XmppConnectionPtr, Stanza.GetStanzaPtr());
return true;
}
void FStropheWebsocketConnection::Tick()
{
// Handles timeouts, processes timers
if (ensureMsgf(Context.GetContextPtr() != nullptr, TEXT("xmpp_ctx_t is null")))
{
xmpp_run_once(Context.GetContextPtr(), 0);
}
}
void FStropheWebsocketConnection::Disconnect()
{
xmpp_disconnect(XmppConnectionPtr);
xmpp_handler_delete(XmppConnectionPtr, StropheWebsocketStanzaEventHandler);
}
void FStropheWebsocketConnection::WebsocketConnectHandler(void* const Userdata)
{
FStropheWebsocketConnection* const Websocket = static_cast<FStropheWebsocketConnection* const>(Userdata);
Websocket->WebsocketConnect();
}
void FStropheWebsocketConnection::WebsocketCloseHandler(void* const Userdata)
{
FStropheWebsocketConnection* const Websocket = static_cast<FStropheWebsocketConnection* const>(Userdata);
Websocket->WebsocketClose();
}
void FStropheWebsocketConnection::WebsocketSendHandler(const char* const Data, const size_t Length, void* const Userdata)
{
FStropheWebsocketConnection* const Websocket = static_cast<FStropheWebsocketConnection* const>(Userdata);
Websocket->WebsocketSend(Data, Length);
}
void FStropheWebsocketConnection::WebsocketConnect()
{
Websocket->Connect();
}
void FStropheWebsocketConnection::WebsocketClose()
{
Websocket->Close();
}
void FStropheWebsocketConnection::WebsocketSend(const char* const Data, const size_t Length)
{
Websocket->Send(Data, Length);
}
void FStropheWebsocketConnection::OnWebsocketConnected()
{
xmpp_extsock_connected(XmppConnectionPtr);
}
void FStropheWebsocketConnection::OnWebsocketConnectionError(const FString& Error)
{
xmpp_extsock_connection_error(XmppConnectionPtr, TCHAR_TO_UTF8(*Error));
}
void FStropheWebsocketConnection::OnWebsocketClosed(int32 StatusCode, const FString& Reason, bool bWasClean)
{
Disconnect();
}
void FStropheWebsocketConnection::OnRawMessage(const void* Data, SIZE_T Size, SIZE_T BytesRemaining)
{
xmpp_extsock_receive(XmppConnectionPtr, static_cast<const char*>(Data), Size);
if (BytesRemaining == 0)
{
// need to reset the parser on message boundaries because xmpp websockets have each message
// as an xml document instead of the full stream being parsable as a single document
xmpp_extsock_parser_reset(XmppConnectionPtr);
}
}
#endif