123 : _loop{&loop}, _core{std::make_shared<Core>(loop, server, cfg)}, _requestedPort{
port} {}
132 : _server{&server}, _requestedPort{
port}, _cfg{cfg} {}
147 _loop->runAndWait([core = _core] {
149 core->server =
nullptr;
163 _ownedLoop = std::make_unique<::morph::exec::IoLoop>();
164 }
catch (
const std::exception&) {
167 _loop = _ownedLoop.get();
168 _core = std::make_shared<Core>(*_ownedLoop, *_server, _cfg);
170 bool listening =
false;
171 _loop->runAndWait([core = _core,
port = _requestedPort, &listening] { listening = core->listen(
port); });
178 [[nodiscard]] std::uint16_t
port()
const {
return _core ? _core->port.load() : std::uint16_t{0}; }
195 _loop->runAndWait([core = _core] { core->close(); });
202 std::shared_ptr<::morph::net::detail::LoopConnection> conn;
214 struct Core : std::enable_shared_from_this<Core> {
215 Core(::morph::exec::IoLoop& ioLoop, ::morph::backend::RemoteServer& remote,
Config config)
216 : loop{ioLoop}, server{&remote}, cfg{config} {}
218 void note(
char const* site)
const noexcept {
219 ::morph::exec::detail::noteOwner(site, loop.loop(), loop.runningHere());
222 bool listen(std::uint16_t requestedPort) {
223 note(
"SocketServer::listen");
224 if (listener || server ==
nullptr) {
227 ::morph::net::detail::TcpSocket bound;
229 bound = ::morph::net::detail::TcpSocket::listen(requestedPort, cfg.backlog);
230 }
catch (
const std::exception&) {
233 if (!bound.setNonBlocking()) {
236 auto adopted = ::core::net::adoptListener(loop.loop(), bound.nativeHandle());
240 static_cast<void>(bound.release());
241 listener = std::move(*adopted);
242 port.store(listener->boundPort());
244 loop.loop().spawn(acceptFlow(shared_from_this(), listener.get(), generation));
249 note(
"SocketServer::close");
259 for (
auto const& client : std::exchange(clients, {})) {
260 finish(*client,
false);
265 void finish(Client& client,
bool flush)
const {
271 if (server !=
nullptr) {
272 server->closeConnection(client.cid);
275 ::morph::net::detail::closeAfterFlush(client.conn);
277 ::morph::net::detail::closeConnection(*client.conn);
281 void forget(Client
const& client) {
283 std::erase_if(clients, [&client](std::shared_ptr<Client>
const& entry) {
return entry.get() == &client; });
284 MORPH_PLOT(
"SocketServer::connections", clients.size());
287 void accepted(std::unique_ptr<::core::net::ISocket> socket) {
289 note(
"SocketServer::accept");
290 if (server ==
nullptr) {
293 auto const cid = server->openConnection();
294 auto conn = std::make_shared<::morph::net::detail::LoopConnection>(loop.loop(), std::move(socket),
298 conn->onDrained = [weak = weak_from_this(), cid] {
299 if (
auto const self = weak.lock(); self &&
self->server !=
nullptr) {
300 self->server->connectionDrained(cid);
303 auto client = std::make_shared<Client>(Client{.conn = std::move(conn), .cid = cid, .done =
false});
304 clients.push_back(client);
305 MORPH_PLOT(
"SocketServer::connections", clients.size());
306 loop.loop().spawn(clientFlow(shared_from_this(), client));
312 [[nodiscard]] ::morph::backend::ConnectionWriter writerFor(std::shared_ptr<Client>
const& client)
const {
313 std::weak_ptr<Client>
const weak = client;
314 auto const counter = client->conn->pending;
316 [loopHandle = loop.weak(), weak, counter](std::string
const& envelope) {
317 if (!sendEnvelope(loopHandle, weak, counter, envelope)) {
318 throw std::runtime_error(
"the connection's I/O loop is gone");
321 .backlog = [counter] {
return counter->load(); }};
331 static bool sendEnvelope(::morph::exec::IoLoop::Weak
const& loopHandle, std::weak_ptr<Client>
const& weak,
332 std::shared_ptr<std::atomic<std::size_t>>
const& counter,
333 std::string
const& envelope) {
336 ::morph::net::detail::encodeWsFrame(::morph::net::detail::WsOpcode::kText, envelope,
false);
337 std::size_t
const counted = envelope.size();
340 counter->fetch_add(counted);
341 bool const posted = loopHandle.
post([weak, counter, counted, frame = std::move(frame)]()
mutable {
343 auto const target = weak.lock();
345 counter->fetch_sub(counted);
348 ::morph::exec::detail::noteOwner(
"SocketServer::reply", target->conn->loop,
349 ::morph::exec::runningOn(target->conn->loop));
352 static_cast<void>(::morph::net::detail::enqueueFrame(target->conn, std::move(frame), counted));
355 counter->fetch_sub(counted);
367 void listenerLost() {
368 note(
"SocketServer::listenerLost");
376 static ::core::async::Task<void> acceptFlow(std::shared_ptr<Core> self, ::core::net::IListener* accepting,
377 std::uint64_t startedAt) {
379 auto next =
co_await accepting->accept();
380 if (
self->generation != startedAt) {
384 if (next.error().code == ::core::net::NetErrorCode::Cancelled) {
387 if (::morph::net::detail::acceptListenerIsDead(next.error())) {
388 self->listenerLost();
394 co_await self->loop.loop().delay(std::chrono::milliseconds{50});
395 if (
self->generation != startedAt) {
400 self->accepted(std::move(*next));
405 static ::core::async::Task<void> clientFlow(std::shared_ptr<Core> self, std::shared_ptr<Client> client) {
406 auto const connection = client->conn;
407 auto header = co_await ::morph::net::detail::readHeaderBlockAsync(connection,
self->cfg.handshakeTimeout);
411 std::optional<std::string> leftover;
417 auto const request = ::morph::net::detail::parseClientHandshakeRequest(header->header);
418 static_cast<void>(::morph::net::detail::enqueueFrame(
419 connection, ::morph::net::detail::buildServerHandshakeResponse(
request.key)));
420 leftover = std::move(header->leftover);
424 if (
self->server !=
nullptr) {
425 self->server->setConnectionWriter(client->cid,
self->writerFor(client));
427 }
catch (
const std::exception&) {
435 ::morph::net::detail::WsFrameReader reader{
true};
436 reader.feed(*leftover);
437 std::array<std::byte, 4096> buf{};
439 auto const outcome =
self->drainFrames(client, reader);
440 if (outcome != Drain::More) {
441 flush = outcome == Drain::Closed;
444 auto const got =
co_await connection->socket->read(buf);
445 if (client->done || !got || *got == 0) {
449 reader.feed(std::string_view{
reinterpret_cast<char const*
>(buf.data()), *got});
455 self->finish(*client, flush);
456 self->forget(*client);
459 enum class Drain : std::uint8_t {
465 Drain drainFrames(std::shared_ptr<Client>
const& client, ::morph::net::detail::WsFrameReader& reader)
const {
467 using ::morph::net::detail::WsOpcode;
469 std::optional<::morph::net::detail::WsFrame> frame;
471 frame = reader.tryExtractFrame();
472 }
catch (
const std::exception&) {
473 return Drain::Failed;
478 if (frame->opcode == WsOpcode::kClose) {
479 static_cast<void>(::morph::net::detail::enqueueFrame(
480 client->conn, ::morph::net::detail::encodeWsFrame(WsOpcode::kClose, frame->payload,
false)));
481 return Drain::Closed;
483 if (frame->opcode == WsOpcode::kPing) {
484 static_cast<void>(::morph::net::detail::enqueueFrame(
485 client->conn, ::morph::net::detail::encodeWsFrame(WsOpcode::kPong, frame->payload,
false)));
488 if (frame->opcode == WsOpcode::kText && server !=
nullptr) {
489 dispatch(client, frame->payload);
491 return Drain::Failed;
501 void dispatch(std::shared_ptr<Client>
const& client, std::string
const& payload)
const {
502 std::weak_ptr<Client>
const weak = client;
505 [loopHandle = loop.weak(), weak, counter = client->conn->pending](
const std::string& reply) {
506 static_cast<void>(sendEnvelope(loopHandle, weak, counter, reply));
511 ::morph::exec::IoLoop& loop;
513 ::morph::backend::RemoteServer* server;
515 std::unique_ptr<::core::net::IListener> listener;
516 std::vector<std::shared_ptr<Client>> clients;
519 std::uint64_t generation{0};
521 std::atomic<std::uint16_t> port{0};
526 std::unique_ptr<::morph::exec::IoLoop> _ownedLoop;
527 ::morph::exec::IoLoop* _loop{
nullptr};
528 std::shared_ptr<Core> _core;
529 ::morph::backend::RemoteServer* _server{
nullptr};
530 std::uint16_t _requestedPort;