/* * (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 #include #include #include #include #include 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_