Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/include/rti/apputils/http/server/detail/CivetWebSocketHandlerImpl.hpp
T

166 lines
5.8 KiB
C++

/*
* (c) Copyright, Real-Time Innovations, 2024.
* All rights reserved.
*
* No duplications, whole or partial, manual or electronic, may be made
* without express written permission. Any such copies, or
* revisions thereof, must display this notice unaltered.
* This code contains trade secrets of Real-Time Innovations, Inc.
*/
#ifndef CIVETWEB_WEBSOCKET_HANDLER_H
#define CIVETWEB_WEBSOCKET_HANDLER_H
#include <CivetServer.h>
#include <civetweb.h>
#include <rti/apputils/http/server/CivetWebSocketConnection.hpp>
#include <rti/apputils/http/server/detail/WebSocketHandler.hpp>
namespace rti { namespace apputils { namespace websocket { namespace civetweb {
/**
* This class implements the CivetWebSocketHandler interface, which is part
* of the CivetWeb API. This class has a WebSocketHandler. It allows us
* to isolate the WebSocket handling that CivetWeb does and the WebSocket
* handling that our WebSocketAPI implements
*/
class CivetWebSocketHandlerImpl : public CivetWebSocketHandler {
public:
CivetWebSocketHandlerImpl(WebSocketHandler& handler) : handler_(handler)
{ }
/**
* This method handles new client connections to the server. For such
* purpose, it creates a new `WebSocketConnection` and attaches it to the
* internal CivetWeb `mg_connection`. This mechanisms allows existing
* connections to be reused in the future.
* @param server CivetWeb server. Unused parameter.
* @param conn CivetWeb connection.
* @return Boolean value indicating the success or failure of the operation.
*/
bool handleConnection(CivetServer *, const struct mg_connection *conn)
{
WebSocketConnection *client_conn =
new CivetWebSocketConnection(const_cast<mg_connection *>(conn));
handler_.handle_connection(*client_conn);
mg_set_user_connection_data(const_cast<mg_connection *>(conn), client_conn);
/*
* client_conn is not being leaked here, it's stored indirectly
* in the user connection data that can be get by calling
* mg_get_user_connection_data as we can see on handleClose.
*/
/* coverity[leaked_storage : FALSE] */
return true;
}
/**
* This method is called when a client connection has ended the handshake
* protocol with success.
* @param server CivetWeb server. Unused parameter.
* @param conn CivetWeb connection
*/
void handleReadyState(CivetServer *, struct mg_connection *conn)
{
WebSocketConnection *client = static_cast<WebSocketConnection *>(
mg_get_user_connection_data(conn));
handler_.handle_ready_state(*client);
}
/**
* This method is called every time a client sends a message to the server
* through the WebSocket connection.
* @param server CivetWeb server. Unused parameter
* @param conn CivetWeb connection
* @param bits A byte with the kind of data (TEXT, PONG, ...)
* @param data String containing the received message, it is not
* null-terminated
* @param data_size String size, the size of the string without the null
* character.
*/
bool handleData(
CivetServer *,
struct mg_connection *conn,
int bits,
char *data,
size_t data_len)
{
StringBuffer msg;
WebSocketConnection *client = static_cast<WebSocketConnection *>(
mg_get_user_connection_data(conn));
// The last byte of the bits say what kind of message it is
switch (((unsigned char) bits) & 0x0F) {
case MG_WEBSOCKET_OPCODE_TEXT:
// This marks the end of the string
*(data + data_len) = '\0';
msg = StringBuffer(data);
try {
handler_.handle_data(*client, msg);
} catch (const RequestException& ex) {
DDSLog_exception(&RTI_LOG_ANY_FAILURE_s, ex.what());
client->send_request_error(ex);
} catch (const rti::apputils::http::HttpException& ex) {
DDSLog_exception(&RTI_LOG_ANY_FAILURE_s, ex.what());
client->send_error(ex);
} catch (...) {
std::string message = "Unexpected exception. The connection will be closed.";
DDSLog_exception(&RTI_LOG_ANY_FAILURE_s, message.c_str());
message.append(": ");
message.append(RTI_FUNCTION_NAME);
client->send(message);
// Close the connection (Especially to avoid vulnerabilities)
client->state(WebSocketStateKind::CLOSE);
client->close();
}
break;
case MG_WEBSOCKET_OPCODE_PING:
// This marks the end of the string
data[data_len] = '\0';
msg = StringBuffer(data);
// just send back the message data in a pong frame
client->send_pong(msg);
break;
default:
break;
}
return true;
}
/**
* This method is called when a client closes the connection. The method
* deletes the connection object.
* @param server CivetWeb Server
* @param conn CivetWeb connection
*/
void handleClose(CivetServer *, const struct mg_connection *conn)
{
struct mg_context *ctx = mg_get_context(conn);
WebSocketConnection *client =
static_cast<WebSocketConnection *>(mg_get_user_connection_data(conn));
if (client->state() != WebSocketStateKind::CLOSE){
handler_.handle_close(*client);
}
mg_lock_context(ctx);
delete client;
mg_set_user_connection_data(const_cast<mg_connection *>(conn), NULL);
mg_unlock_context(ctx);
}
~CivetWebSocketHandlerImpl()
{
}
private:
WebSocketHandler& handler_;
};
}}}} // namespace rti::webdds::websocket::civetweb
#endif