#define MS_CLASS "TcpServerHandler"
#include "handles/TcpServerHandler.hpp"
#include "Logger.hpp"
#include "MediaSoupErrors.hpp"
#include "Utils.hpp"
inline static void onConnection(uv_stream_t* handle, int status)
{
auto* server = static_cast<TcpServerHandler*>(handle->data);
if (server)
server->OnUvConnection(status);
}
inline static void onClose(uv_handle_t* handle)
{
delete handle;
}
TcpServerHandler::TcpServerHandler(uv_tcp_t* uvHandle, int backlog) : uvHandle(uvHandle)
{
MS_TRACE();
int err;
this->uvHandle->data = static_cast<void*>(this);
err = uv_listen(
reinterpret_cast<uv_stream_t*>(this->uvHandle),
backlog,
static_cast<uv_connection_cb>(onConnection));
if (err != 0)
{
uv_close(reinterpret_cast<uv_handle_t*>(this->uvHandle), static_cast<uv_close_cb>(onClose));
MS_THROW_ERROR("uv_listen() failed: %s", uv_strerror(err));
}
if (!SetLocalAddress())
{
uv_close(reinterpret_cast<uv_handle_t*>(this->uvHandle), static_cast<uv_close_cb>(onClose));
MS_THROW_ERROR("error setting local IP and port");
}
}
TcpServerHandler::~TcpServerHandler()
{
MS_TRACE();
if (!this->closed)
Close();
}
void TcpServerHandler::Close()
{
MS_TRACE();
if (this->closed)
return;
this->closed = true;
this->uvHandle->data = nullptr;
MS_DEBUG_DEV("closing %zu active connections", this->connections.size());
for (auto* connection : this->connections)
{
delete connection;
}
uv_close(reinterpret_cast<uv_handle_t*>(this->uvHandle), static_cast<uv_close_cb>(onClose));
}
void TcpServerHandler::Dump() const
{
MS_DUMP("<TcpServerHandler>");
MS_DUMP(
" [TCP, local:%s :%" PRIu16 ", status:%s, connections:%zu]",
this->localIp.c_str(),
static_cast<uint16_t>(this->localPort),
(!this->closed) ? "open" : "closed",
this->connections.size());
MS_DUMP("</TcpServerHandler>");
}
void TcpServerHandler::AcceptTcpConnection(TcpConnectionHandler* connection)
{
MS_TRACE();
MS_ASSERT(connection != nullptr, "TcpConnectionHandler pointer was not allocated by the user");
try
{
connection->Setup(this, &(this->localAddr), this->localIp, this->localPort);
}
catch (const MediaSoupError& error)
{
delete connection;
return;
}
int err = uv_accept(
reinterpret_cast<uv_stream_t*>(this->uvHandle),
reinterpret_cast<uv_stream_t*>(connection->GetUvHandle()));
if (err != 0)
MS_ABORT("uv_accept() failed: %s", uv_strerror(err));
try
{
connection->Start();
}
catch (const MediaSoupError& error)
{
delete connection;
return;
}
this->connections.insert(connection);
}
bool TcpServerHandler::SetLocalAddress()
{
MS_TRACE();
int err;
int len = sizeof(this->localAddr);
err =
uv_tcp_getsockname(this->uvHandle, reinterpret_cast<struct sockaddr*>(&this->localAddr), &len);
if (err != 0)
{
MS_ERROR("uv_tcp_getsockname() failed: %s", uv_strerror(err));
return false;
}
int family;
Utils::IP::GetAddressInfo(
reinterpret_cast<const struct sockaddr*>(&this->localAddr), family, this->localIp, this->localPort);
return true;
}
inline void TcpServerHandler::OnUvConnection(int status)
{
MS_TRACE();
if (this->closed)
return;
if (status != 0)
{
MS_ERROR("error while receiving a new TCP connection: %s", uv_strerror(status));
return;
}
UserOnTcpConnectionAlloc();
}
inline void TcpServerHandler::OnTcpConnectionClosed(TcpConnectionHandler* connection)
{
MS_TRACE();
MS_DEBUG_DEV("TCP connection closed");
this->connections.erase(connection);
UserOnTcpConnectionClosed(connection);
delete connection;
}