/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuNetSocketServer.cpp Date: 2022-8-22 Author: Reece ***/ #include "Networking.hpp" #include "AuNetSocketServer.hpp" #include "AuNetEndpoint.hpp" #include "AuNetError.hpp" #include "AuNetWorker.hpp" namespace Aurora::IO::Net { SocketServer::SocketServer(NetInterface *pInterface, NetWorker *pWorker, const AuSPtr &pDriver, const AuSPtr &pFactory, AuUInt32 uMaxConnections, AuUInt32 uDefaultInputStreamSize, bool bMultiThreaded) : Socket(pInterface, pWorker, AuSPtr{}, -1), pDriver_(pDriver), pFactory_(pFactory), uMaxConnections_(uMaxConnections), uDefaultInputStreamSize(uDefaultInputStreamSize), bMultiThreaded(bMultiThreaded) { } void SocketServer::Init(const NetEndpoint &localAddress) { if (!this->InitSocket(localAddress)) { this->SendErrorNoStream(ENetworkError::eInitSocketFailed); return; } } void SocketServer::Listen(const NetEndpoint &localAddress, bool bBind, bool bListen) { if (this->bHasErrored_) { return; } this->localEndpoint_ = localAddress; this->endpointSize_ = OptimizeEndpoint(this->localEndpoint_); if (!this->endpointSize_) { this->SendErrorNoStream(ENetworkError::eBadAddress); return; } if (bBind) { if (!this->ImplBind()) { this->SendErrorNoStream(ENetworkError::eUnknown); return; } } if (bListen) { if (!this->ImplListen()) { this->SendErrorNoStream(ENetworkError::eUnknown); return; } } if ((!AuBuild::kIsNtDerived) || // NT accept doesnt need it (this->localEndpoint_.transportProtocol == ETransportProtocol::eProtocolUDP)) // only the recvfrom loop does { if (!this->MakeNonblocking()) { NetError error; NetError_SetCurrent(error); this->SendErrorNoStream(error); return; } } if (this->pDriver_) { try { this->pDriver_->OnBind(); } catch (...) { SysPushErrorCatch(); this->SendErrorNoStream(ENetworkError::eAsyncError); } } } void SocketServer::Accept() { if (this->bHasErrored_) { return; } if (!this->BeginAcceptLoop()) { this->SendErrorNoStream(NetError(ENetworkError::eCantAccept)); return; } } ///////////////////////////////////////////////////////////////////////////////////// // ISocketServer AuSPtr SocketServer::GetServerDriver() { return this->pDriver_; } AuSPtr SocketServer::GetFactory() { return this->pFactory_; } AuSPtr SocketServer::GetServerRecvStats() { return AuSPtr(this->SharedFromThis(), &this->recvStats_); } AuSPtr SocketServer::GetServerSendStats() { return AuSPtr(this->SharedFromThis(), &this->sendStats_); } AuSPtr SocketServer::GetLimits() { return AuSPtr(this->SharedFromThis(), &this->serverLimits_); } AuList> SocketServer::GetChildren() { AuList> ret; AU_LOCK_GUARD(this->childrenMutex); for (const auto &pChild : this->childrenSockets) { try { ret.push_back(pChild->SharedFromThis()); } catch (...) { } } return ret; } AuUInt32 SocketServer::GetSessionCount() { return this->childrenSockets.size(); } void SocketServer::OnNotifyChildCreated(SocketBase *pSocket) { { AU_LOCK_GUARD(this->childrenMutex); this->childrenSockets.push_back(pSocket); } } void SocketServer::OnNotifyChildRemoved(SocketBase *pSocket) { { AU_LOCK_GUARD(this->childrenMutex); AuTryRemove(this->childrenSockets, pSocket); } this->serverLimits_.NotifyChildRemoved(); } ///////////////////////////////////////////////////////////////////////////////////// // ISocketDriver bool SocketServer::OnPreestablish(const AuSPtr &pInforming) { return true; } void SocketServer::OnEstablish() { } void SocketServer::OnStreamUpdated() { } void SocketServer::OnFatalErrorReported(const NetError &error) { if (this->pDriver_) { this->pDriver_->OnFatalErrorReported(error); } } void SocketServer::OnEnd() { } void SocketServer::OnFinalize() { if (this->pDriver_) { this->pDriver_->OnFinalize(); } } ///////////////////////////////////////////////////////////////////////////////////// // SocketBase void SocketServer::FinishConstructAsync() { } void SocketServer::Shutdown(bool bNow) { Socket::Shutdown(bNow); } void SocketServer::Destroy() { Socket::Destroy(); } void SocketServer::ScheduleAcceptTick() { // WARNING: Accepts cannot be on the same tick as on-accept // We must reschedule to prevent add-source, under on tick callback, prior to removal eval of the very same event handle // (loopqueue constraint) auto shared = AuDynamicCast(this->SharedFromThis()); // C++ is cringe. cant static-up-cast a virtual base if (!this->ToWorkerEx()->TryScheduleInternalTemplate([that = shared](const AuSPtr> &info) { that->DoNonblockingReadTick(); }, AuSPtr>{})) { this->SendErrorBeginShutdown({}); } } bool SocketServer::PrepareConnectOperations() { return false; } bool SocketServer::UpdateLocalEndpoint() { socklen_t iLen { (int)this->endpointSize_ }; if (!pgetsockname) { return false; } if (pgetsockname(this->osHandle_, (sockaddr *)this->localEndpoint_.hint, &iLen) == -1) { SysPushErrorIO(); return false; } DeoptimizeEndpoint(this->localEndpoint_); return true; } bool SocketServer::UpdateRemoteEndpoint() { return false; } bool SocketServer::TryBindAnyLocal() { return false; } bool SocketServer::ConnectOverlapped() { return false; } bool SocketServer::ConnectNonblocking() { return false; } bool SocketServer::ConnectBlocking() { return false; } }