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

321 lines
11 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 _WEBSOCKET_CONNECTION_H_
#define _WEBSOCKET_CONNECTION_H_
#include <civetweb.h>
#include <map>
#include <rti/apputils/StringBuffer.hpp>
#include <rti/apputils/http/common/WebSocketUtils.h>
#include <rti/apputils/http/common/StatusCode.hpp>
#include <rti/apputils/http/common/HttpException.hpp>
namespace rti { namespace apputils { namespace websocket {
/*
* This class wraps the state of the client connection. Every instance created
* from this class must be kept alive until the connection is closed. Whenever
* the server receives a new web_wait_factorysocket connection attempt, it
* creates a WebSocketConnection instance and this WebSocketConnection will be
* attached to the client connection.
*
* This class has the state of the client connection, the type of content that
* the client accepts and the bind ids that each connection have bound.
*/
class WebSocketConnection {
public:
explicit WebSocketConnection()
: state_(WebSocketStateKind::UNKNOWN),
content_type_(WebSocketContentType::UNKNOWN)
{ }
virtual ~WebSocketConnection() = default;
/**
* Overloading the == operator to compare connections.
* @param conn A pointer to the connection to compare
*/
virtual bool operator==(WebSocketConnection *conn) = 0;
/**
* This method returns the state of the connection
* @return WebSocketStateKind the current status
*/
WebSocketStateKind state() const
{
return state_;
}
/**
* This method returns the content type accepted for the connection
* @return WebSocketContentType
*/
WebSocketContentType content_type() const
{
return content_type_;
}
/**
* This method sets the state of the connection
* @param state WebSocketStateKind the new status
*/
void state(const WebSocketStateKind& state)
{
state_ = state;
}
/**
* This method sets the content type accepted for the connection
* @param content WebSocketContentType
*/
void content_type(const WebSocketContentType& content)
{
content_type_ = content;
}
/**
* This function sends a MG_WEBSOCKET_OPCODE_TEXT message to the user.
* @param msg The message string to be sent
*/
virtual void send(const StringBuffer& msg) = 0;
/**
* This function sends a MG_WEBSOCKET_OPCODE_PONG message to the user.
* @param msg The message string to be sent
*/
virtual void send_pong(const StringBuffer& msg) = 0;
/**
* This function creates the error message to the user.
* @param ex Excetion with the info of an issue
* @param save_ctx context with that point to the message
*/
void create_error(
RTIXMLSaveContext& save_ctx,
const rti::apputils::http::HttpException& ex)
{
struct DDS_PrintFormat format = DDS_PrintFormat_INITIALIZER;
DDS_PrintFormatProperty properties = DDS_PrintFormatProperty_INITIALIZER;
DDS_ReturnCode_t retcode = DDS_RETCODE_ERROR;
if (content_type_ == WebSocketContentType::JSON) {
properties.kind = DDS_JSON_PRINT_FORMAT;
} else if (content_type_ == WebSocketContentType::XML) {
properties.kind = DDS_XML_PRINT_FORMAT;
} else {
properties.kind = DDS_XML_PRINT_FORMAT;
}
retcode = DDS_PrintFormatProperty_to_print_format(&properties, &format);
if (retcode != DDS_RETCODE_OK) {
RTI_THROW_HTTP_EXCEPTION(
rti::apputils::http::StatusCode::internal_server_error,
&RTI_LOG_ANY_FAILURE_s,
"Unsupported content type");
}
format.print_top_level_beginning(&format, &save_ctx, "error", 0);
format.print_primitive_type_beginning(&format, &save_ctx, "code", 1);
RTIXMLSaveContext_freeform(
&save_ctx,
"%d",
ex.error_code());
format.print_primitive_type_ending(&format, &save_ctx, "code", 1);
RTIXMLSaveContext_freeform(&save_ctx, "%s", format.element_separator);
format.print_primitive_type_beginning(&format, &save_ctx, "message", 1);
RTIXMLSaveContext_freeform(
&save_ctx,
"%sError %s%s",
format.string_opening_delimiter,
ex.what(),
format.string_closing_delimiter);
format.print_primitive_type_ending(&format, &save_ctx, "message", 1);
RTIXMLSaveContext_freeform(&save_ctx, "%s", format.element_separator);
format.print_primitive_type_beginning(&format, &save_ctx, "method", 1);
RTIXMLSaveContext_freeform(
&save_ctx,
"%s%s%s",
format.string_opening_delimiter,
RTI_FUNCTION_NAME,
format.string_closing_delimiter);
format.print_primitive_type_ending(&format, &save_ctx, "method", 1);
format.print_top_level_ending(&format, &save_ctx, "error", 0);
}
/**
* This function sends an error message to the user.
* @param msg The error message that the error will have
*/
void send_error(const rti::apputils::http::HttpException& ex)
{
StringBuffer buffer;
RTIXMLSaveContext save_ctx = RTIXMLSaveContext_INITIALIZER;
save_ctx.sout = (char*) buffer.c_str();
if (!RTIOsapiUtility_sizeToUInt32(save_ctx.ssize, buffer.capacity())) {
RTI_THROW_HTTP_EXCEPTION(
rti::apputils::http::StatusCode::internal_server_error,
&RTI_LOG_ANY_FAILURE_s,
"Error allocating string");
}
create_error(save_ctx, ex);
if (save_ctx.outputStringLength + 1 > buffer.capacity()) {
buffer.ensure_capacity(save_ctx.outputStringLength + 1);
// Reset save_context
RTIXMLSaveContext_initialize(&save_ctx);
save_ctx.sout = (char *) buffer.c_str();
if (!RTIOsapiUtility_sizeToUInt32(save_ctx.ssize, buffer.capacity())) {
RTI_THROW_HTTP_EXCEPTION(
rti::apputils::http::StatusCode::internal_server_error,
&RTI_LOG_ANY_FAILURE_s,
"Error allocating string");
}
create_error(save_ctx, ex);
}
send(buffer);
}
/**
* This function creates the request error message to the user.
* @param ex RequestExcetion with the info of an issue
* @param save_ctx context with that point to the message
*/
void create_request_error(
RTIXMLSaveContext& save_ctx,
const RequestException& ex)
{
struct DDS_PrintFormat format = DDS_PrintFormat_INITIALIZER;
DDS_PrintFormatProperty properties = DDS_PrintFormatProperty_INITIALIZER;
DDS_ReturnCode_t retcode = DDS_RETCODE_ERROR;
if (content_type_ == WebSocketContentType::JSON) {
properties.kind = DDS_JSON_PRINT_FORMAT;
} else if (content_type_ == WebSocketContentType::XML) {
properties.kind = DDS_XML_PRINT_FORMAT;
} else {
properties.kind = DDS_XML_PRINT_FORMAT;
}
retcode = DDS_PrintFormatProperty_to_print_format(&properties, &format);
if (retcode != DDS_RETCODE_OK) {
RTI_THROW_HTTP_EXCEPTION(
rti::apputils::http::StatusCode::internal_server_error,
&RTI_LOG_ANY_FAILURE_s,
"Unsupported content type");
}
format.print_top_level_beginning(
&format,
&save_ctx,
WEBSOCKET_RESPONSE_NAME,
0);
format.print_primitive_type_beginning(&format, &save_ctx, "return_code", 1);
RTIXMLSaveContext_freeform(
&save_ctx,
"%d",
ex.error_code());
format.print_primitive_type_ending(&format, &save_ctx, "return_code", 1);
RTIXMLSaveContext_freeform(&save_ctx, "%s", format.element_separator);
format.print_primitive_type_beginning(&format, &save_ctx, "error", 1);
RTIXMLSaveContext_freeform(
&save_ctx,
"%s%s%s",
format.string_opening_delimiter,
ex.what(),
format.string_closing_delimiter);
format.print_primitive_type_ending(&format, &save_ctx, "error", 1);
RTIXMLSaveContext_freeform(&save_ctx, "%s", format.element_separator);
format.print_primitive_type_beginning(&format, &save_ctx, "method", 1);
RTIXMLSaveContext_freeform(
&save_ctx,
"%s%s%s",
format.string_opening_delimiter,
ex.get_method_name(),
format.string_closing_delimiter);
format.print_primitive_type_ending(&format, &save_ctx, "method", 1);
format.print_top_level_ending(
&format,
&save_ctx,
WEBSOCKET_RESPONSE_NAME,
0);
}
/**
* This function sends an REQUEST error message to the user.
* @param msg The error message that the error will have
*/
void send_request_error(const RequestException& ex)
{
StringBuffer buffer;
RTIXMLSaveContext save_ctx = RTIXMLSaveContext_INITIALIZER;
save_ctx.sout = (char*) buffer.c_str();
if (!RTIOsapiUtility_sizeToUInt32(save_ctx.ssize, buffer.capacity())) {
RTI_THROW_HTTP_EXCEPTION(
rti::apputils::http::StatusCode::internal_server_error,
&RTI_LOG_ANY_FAILURE_s,
"Error allocating string");
}
create_request_error(save_ctx, ex);
if (save_ctx.outputStringLength + 1 > buffer.capacity()) {
buffer.ensure_capacity(save_ctx.outputStringLength + 1);
// Reset save_context
RTIXMLSaveContext_initialize(&save_ctx);
save_ctx.sout = (char *) buffer.c_str();
if (!RTIOsapiUtility_sizeToUInt32(save_ctx.ssize, buffer.capacity())) {
RTI_THROW_HTTP_EXCEPTION(
rti::apputils::http::StatusCode::internal_server_error,
&RTI_LOG_ANY_FAILURE_s,
"Error allocating string");
}
create_request_error(save_ctx, ex);
}
send(buffer);
}
/**
* This function close a WebSocketConnection.
* @param exception with the error message that happend
*/
virtual void close() = 0;
private:
WebSocketStateKind state_;
WebSocketContentType content_type_;
};
}}} // namespace rti::webdds::websocket
#endif // _WEBSOCKET_CONNECTION_H_