Files
Renderive/web_server/app/Renderive_WebSocket_Controller.cpp
T
2026-08-18 11:57:43 +08:00

69 lines
3.7 KiB
C++

#include "Renderive_WebSocket_Controller.h"
#include "Asio_WebSocket_Scene_Loop.h"
#include "Web_Event_Adapter.h"
#include "Web_Plot_Session.h"
#include <renderive/error/Error_Policy.hpp>
#include <trantor/utils/Logger.h>
#include <chrono>
#include <memory>
#include <utility>
namespace renderive::web {
namespace {
using Scene_Loop = Asio_WebSocket_Scene_Loop<Web_Plot_Session>;
void send_response(const std::weak_ptr<drogon::WebSocketConnection>& weak_connection, Web_Response response) {
const auto connection = weak_connection.lock();
if (!connection || !connection->connected())
return;
const auto message_type = response.type == Web_Response_Type::Pixels ? drogon::WebSocketMessageType::Binary : drogon::WebSocketMessageType::Text;
connection->send(response.payload.data(), response.payload.size(), message_type);
}
void fail_session(const std::weak_ptr<drogon::WebSocketConnection>& weak_connection, std::exception_ptr exception) {
static_cast<void>(::renderive::error::capture("handling Renderive WebSocket scene loop", exception));
LOG_ERROR << "Renderive WebSocket scene loop failed";
if (const auto connection = weak_connection.lock(); connection && connection->connected())
connection->shutdown(drogon::CloseCode::kUnexpectedCondition, "Renderive rendering failed");
}
}
Renderive_WebSocket_Controller::Renderive_WebSocket_Controller(std::shared_ptr<asio::thread_pool> scene_pool) : scene_pool_(std::move(scene_pool)) {}
void Renderive_WebSocket_Controller::handleNewConnection(const drogon::HttpRequestPtr&, const drogon::WebSocketConnectionPtr& connection) {
const std::weak_ptr<drogon::WebSocketConnection> weak_connection = connection;
connection->setContext(Scene_Loop::create(scene_pool_->get_executor(), [weak_connection](Web_Response response) {
send_response(weak_connection, std::move(response));
}, [weak_connection](std::exception_ptr exception) {
fail_session(weak_connection, exception);
}));
connection->setPingMessage("renderive", std::chrono::seconds(20));
LOG_INFO << "Renderive WebSocket connected: " << connection->peerAddr().toIpPort();
}
void Renderive_WebSocket_Controller::handleNewMessage(const drogon::WebSocketConnectionPtr& connection, std::string&& message, const drogon::WebSocketMessageType& type) {
if (type == drogon::WebSocketMessageType::Ping || type == drogon::WebSocketMessageType::Pong || type == drogon::WebSocketMessageType::Close)
return;
if (type != drogon::WebSocketMessageType::Text) {
connection->shutdown(drogon::CloseCode::kInvalidMessage, "Renderive accepts text events only");
return;
}
if (message.size() > 16 * 1024) {
connection->shutdown(drogon::CloseCode::kMessageTooBig, "Renderive event is too large");
return;
}
const auto event = Web_Event_Adapter::decode(message);
if (!event) {
connection->shutdown(drogon::CloseCode::kWrongMessageContent, "Invalid Renderive event");
return;
}
const auto session = connection->getContext<Scene_Loop>();
if (!session) {
connection->shutdown(drogon::CloseCode::kUnexpectedCondition, "Renderive session is unavailable");
return;
}
if (session->submit(std::move(*event)) == Scene_Loop::Submit_Result::queue_full)
connection->shutdown(drogon::CloseCode::kViolation, "Renderive scene queue is full");
}
void Renderive_WebSocket_Controller::handleConnectionClosed(const drogon::WebSocketConnectionPtr& connection) {
LOG_INFO << "Renderive WebSocket closed: " << connection->peerAddr().toIpPort();
if (const auto session = connection->getContext<Scene_Loop>())
session->stop();
connection->clearContext();
}
}