Loading...
Searching...
No Matches
websocket.hpp
Go to the documentation of this file.
/*
restinio
*/
#pragma once
#include <functional>
#include <restinio/websocket/message.hpp>
#include <restinio/websocket/impl/ws_connection_base.hpp>
#include <restinio/websocket/impl/ws_connection.hpp>
#include <restinio/utils/base64.hpp>
#include <restinio/utils/sha1.hpp>
namespace restinio
{
namespace websocket
{
namespace basic
{
//
// ws_t
//
class ws_t
: public std::enable_shared_from_this< ws_t >
{
public:
//
// activate()
//
friend void activate( ws_t & ws )
{
ws.m_ws_connection_handle->init_read( ws.shared_from_this() );
}
ws_t( const ws_t & ) = delete;
ws_t( ws_t && ) = delete;
ws_t & operator = ( const ws_t & ) = delete;
ws_t & operator = ( ws_t && ) = delete;
ws_t(
impl::ws_connection_handle_t ws_connection_handle,
endpoint_t remote_endpoint )
: m_ws_connection_handle{ std::move( ws_connection_handle ) }
, m_remote_endpoint{ std::move( remote_endpoint ) }
{}
~ws_t()
{
try
{
shutdown();
}
catch( ... )
{}
}
connection_id_t
connection_id() const
{
return m_ws_connection_handle ? m_ws_connection_handle->connection_id() : 0;
}
void
shutdown()
{
if( m_ws_connection_handle )
{
auto con = std::move( m_ws_connection_handle );
con->shutdown();
}
}
void
kill()
{
if( m_ws_connection_handle )
{
auto con = std::move( m_ws_connection_handle );
con->kill();
}
}
void
send_message(
final_frame_flag_t final_flag,
opcode_t opcode,
writable_item_t payload,
write_status_cb_t wscb = write_status_cb_t{} )
{
if( m_ws_connection_handle )
{
if( restinio::writable_item_type_t::trivial_write_operation ==
payload.write_type() )
{
writable_items_container_t bufs;
bufs.reserve( 2 );
// Create header serialize it and append to bufs .
impl::message_details_t details{
final_flag, opcode, asio_ns::buffer_size( payload.buf() ) };
bufs.emplace_back(
impl::write_message_details( details ) );
bufs.emplace_back( std::move( payload ) );
write_group_t wg{ std::move( bufs ) };
if( wscb )
{
wg.after_write_notificator( std::move( wscb ) );
}
// TODO: set flag.
const bool is_close_frame =
opcode_t::connection_close_frame == opcode;
if( is_close_frame )
{
auto con = std::move( m_ws_connection_handle );
con->write_data(
std::move( wg ),
is_close_frame );
}
else
{
m_ws_connection_handle->write_data(
std::move( wg ),
is_close_frame );
}
}
else
{
throw exception_t{ "ws doesn't support sendfile" };
}
}
else
{
throw exception_t{ "websocket is not available" };
}
}
void
send_message( message_t msg, write_status_cb_t wscb = write_status_cb_t{} )
{
send_message(
msg.final_flag(),
msg.opcode(),
writable_item_t{ std::move( msg.payload() ) },
std::move( wscb ) );
}
const endpoint_t & remote_endpoint() const noexcept { return m_remote_endpoint; }
private:
impl::ws_connection_handle_t m_ws_connection_handle;
const endpoint_t m_remote_endpoint;
};
using ws_handle_t = std::shared_ptr< ws_t >;
//
// activation_t
//
enum class activation_t
{
immediate,
delayed
};
//
// upgrade()
//
template <
typename Traits,
typename WS_Message_Handler >
ws_handle_t
upgrade(
generic_request_type_from_traits_t<Traits> & req,
activation_t activation_flag,
http_header_fields_t upgrade_response_header_fields,
WS_Message_Handler ws_message_handler )
{
// TODO: check if upgrade request?
if( !upgrade_response_header_fields.has_field( http_field::sec_websocket_accept ) )
{
throw exception_t{
fmt::format(
RESTINIO_FMT_FORMAT_STRING(
"{} field is mandatory for upgrade response" ),
field_to_string( http_field::sec_websocket_accept ) ) };
}
if( !upgrade_response_header_fields.has_field( http_field::upgrade ) )
{
upgrade_response_header_fields.set_field( http_field::upgrade, "websocket" );
}
using connection_t = restinio::impl::connection_t< Traits >;
auto conn_ptr = std::move( restinio::impl::access_req_connection( req ) );
if( !conn_ptr )
{
throw exception_t{ "no connection for upgrade: already moved" };
}
auto & con = dynamic_cast< connection_t & >( *conn_ptr );
using ws_connection_t = impl::ws_connection_t< Traits, WS_Message_Handler >;
auto upgrade_internals = con.move_upgrade_internals();
auto ws_connection =
std::make_shared< ws_connection_t >(
con.connection_id(),
std::move( upgrade_internals.m_settings ),
std::move( upgrade_internals.m_socket ),
std::move( upgrade_internals.m_lifetime_monitor ),
std::move( ws_message_handler ) );
writable_items_container_t upgrade_response_bufs;
{
http_response_header_t upgrade_response_header{ status_switching_protocols() };
upgrade_response_header.swap_fields( upgrade_response_header_fields );
upgrade_response_header.connection( http_connection_header_t::upgrade );
const auto content_length_flag =
restinio::impl::content_length_field_presence_t::skip_content_length;
upgrade_response_bufs.emplace_back(
restinio::impl::create_header_string(
upgrade_response_header,
content_length_flag ) );
}
ws_connection->write_data(
write_group_t{ std::move( upgrade_response_bufs ) },
false );
auto result =
std::make_shared< ws_t >( std::move( ws_connection ), req.remote_endpoint() );
if( activation_t::immediate == activation_flag )
{
activate( *result );
}
// Returns strong handle on websocket, thus giving an ownership.
return result;
}
template <
typename Traits,
typename WS_Message_Handler >
auto
upgrade(
generic_request_type_from_traits_t<Traits> & req,
activation_t activation_flag,
std::string sec_websocket_accept_field_value,
WS_Message_Handler ws_message_handler )
{
http_header_fields_t upgrade_response_header_fields;
upgrade_response_header_fields.set_field(
http_field::sec_websocket_accept,
std::move( sec_websocket_accept_field_value ) );
return
upgrade< Traits, WS_Message_Handler >(
req,
activation_flag,
std::move( upgrade_response_header_fields ),
std::move( ws_message_handler ) );
}
template <
typename Traits,
typename WS_Message_Handler >
auto
upgrade(
generic_request_type_from_traits_t<Traits> & req,
activation_t activation_flag,
std::string sec_websocket_accept_field_value,
std::string sec_websocket_protocol_field_value,
WS_Message_Handler ws_message_handler )
{
http_header_fields_t upgrade_response_header_fields;
upgrade_response_header_fields.set_field(
http_field::sec_websocket_accept,
std::move( sec_websocket_accept_field_value ) );
upgrade_response_header_fields.set_field(
http_field::sec_websocket_protocol,
std::move( sec_websocket_protocol_field_value ) );
return
upgrade< Traits, WS_Message_Handler >(
req,
activation_flag,
std::move( upgrade_response_header_fields ),
std::move( ws_message_handler ) );
}
template <
typename Traits,
typename WS_Message_Handler >
auto
upgrade(
generic_request_type_from_traits_t<Traits> & req,
activation_t activation_flag,
WS_Message_Handler ws_message_handler )
{
const char * websocket_accept_field_suffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
const auto ws_key =
req.header().get_field( restinio::http_field::sec_websocket_key ) +
websocket_accept_field_suffix;
auto digest = restinio::utils::sha1::make_digest( ws_key );
std::string sec_websocket_accept_field_value = utils::base64::encode(
utils::sha1::to_string( digest ) );
http_header_fields_t upgrade_response_header_fields;
upgrade_response_header_fields.set_field(
http_field::sec_websocket_accept,
std::move( sec_websocket_accept_field_value ) );
return
upgrade< Traits, WS_Message_Handler >(
req,
activation_flag,
std::move( upgrade_response_header_fields ),
std::move( ws_message_handler ) );
}
} /* namespace basic */
} /* namespace websocket */
} /* namespace restinio */
base64.hpp
restinio::exception_t
Exception class for all exceptions thrown by RESTinio.
Definition: exception.hpp:26
restinio::generic_request_t
HTTP Request data.
Definition: request_handler.hpp:209
restinio::generic_request_t::header
const http_request_header_t & header() const noexcept
Get request header.
Definition: request_handler.hpp:264
restinio::generic_request_t::remote_endpoint
const endpoint_t & remote_endpoint() const noexcept
Get the remote endpoint of the underlying connection.
Definition: request_handler.hpp:296
restinio::http_header_fields_t
Header fields map.
Definition: http_headers.hpp:703
restinio::http_header_fields_t::has_field
bool has_field(string_view_t field_name) const noexcept
Check field by name.
Definition: http_headers.hpp:754
restinio::http_header_fields_t::set_field
void set_field(http_header_field_t http_header_field)
Set header field via http_header_field_t.
Definition: http_headers.hpp:773
restinio::http_header_fields_t::get_field
const std::string & get_field(string_view_t field_name) const
Get field by name.
Definition: http_headers.hpp:951
restinio::impl::connection_t
Context for handling http connections.
Definition: connection.hpp:306
restinio::websocket::basic::impl::message_details_t
Websocket message class with more detailed protocol information.
Definition: ws_parser.hpp:63
restinio::websocket::basic::impl::ws_connection_t
Context for handling websocket connections.
Definition: ws_connection.hpp:125
restinio::websocket::basic::message_t
WebSocket message.
Definition: message.hpp:145
restinio::websocket::basic::message_t::final_flag
final_frame_flag_t final_flag() const noexcept
Get final flag.
Definition: message.hpp:168
restinio::websocket::basic::message_t::opcode
opcode_t opcode() const noexcept
Definition: message.hpp:186
restinio::websocket::basic::ws_t
A WebSocket bind.
Definition: websocket.hpp:40
restinio::websocket::basic::ws_t::ws_t
ws_t(ws_t &&)=delete
restinio::websocket::basic::ws_t::send_message
void send_message(message_t msg, write_status_cb_t wscb=write_status_cb_t{})
Definition: websocket.hpp:171
restinio::websocket::basic::ws_t::ws_t
ws_t(impl::ws_connection_handle_t ws_connection_handle, endpoint_t remote_endpoint)
Definition: websocket.hpp:57
restinio::websocket::basic::ws_t::operator=
ws_t & operator=(const ws_t &)=delete
restinio::websocket::basic::ws_t::ws_t
ws_t(const ws_t &)=delete
restinio::websocket::basic::ws_t::m_ws_connection_handle
impl::ws_connection_handle_t m_ws_connection_handle
Definition: websocket.hpp:184
restinio::websocket::basic::ws_t::~ws_t
~ws_t()
Definition: websocket.hpp:64
restinio::websocket::basic::ws_t::connection_id
connection_id_t connection_id() const
Get connection id.
Definition: websocket.hpp:80
restinio::websocket::basic::ws_t::shutdown
void shutdown()
Shutdown websocket: wait for all outgoing data to be sent, and close connection.
Definition: websocket.hpp:88
restinio::websocket::basic::ws_t::kill
void kill()
Kill websocket: close underlying tcp socket. Do not tolerate unsent outgoing data.
Definition: websocket.hpp:100
restinio::websocket::basic::ws_t::activate
friend void activate(ws_t &ws)
Activate websocket: start receiving messages.
Definition: websocket.hpp:47
restinio::websocket::basic::ws_t::remote_endpoint
const endpoint_t & remote_endpoint() const noexcept
Get the remote endpoint of the underlying connection.
Definition: websocket.hpp:181
restinio::websocket::basic::ws_t::m_remote_endpoint
const endpoint_t m_remote_endpoint
Remote endpoint for this ws-connection.
Definition: websocket.hpp:187
restinio::websocket::basic::ws_t::send_message
void send_message(final_frame_flag_t final_flag, opcode_t opcode, writable_item_t payload, write_status_cb_t wscb=write_status_cb_t{})
Send_websocket message.
Definition: websocket.hpp:111
restinio::writable_item_t
Class for storing the buffers used for streaming body (request/response).
Definition: buffers.hpp:526
restinio::writable_item_t::buf
asio_ns::const_buffer buf() const
Create a buf reference object used by ASIO.
Definition: buffers.hpp:624
restinio::writable_item_t::write_type
writable_item_type_t write_type() const noexcept
Get a type of a stored buffer object.
Definition: buffers.hpp:612
restinio::write_group_t
Group of writable items transported to the context of underlying connection as one solid piece.
Definition: buffers.hpp:729
restinio::write_group_t::after_write_notificator
void after_write_notificator(write_status_cb_t notificator) noexcept
Set after write notificator.
Definition: buffers.hpp:838
RESTINIO_FMT_FORMAT_STRING
#define RESTINIO_FMT_FORMAT_STRING(s)
Definition: include_fmtlib.hpp:92
message.hpp
restinio::impl::access_req_connection
connection_handle_t & access_req_connection(generic_request_t< Extra_Data > &) noexcept
Definition: request_handler.hpp:485
restinio::impl::create_header_string
std::string create_header_string(const http_response_header_t &h, content_length_field_presence_t content_length_field_presence=content_length_field_presence_t::add_content_length, std::size_t buffer_size=0)
Creates a string for http response header.
Definition: header_helpers.hpp:68
restinio::impl::content_length_field_presence_t::skip_content_length
@ skip_content_length
restinio::utils::base64::encode
std::string encode(string_view_t str)
Definition: base64.hpp:123
restinio::utils::sha1::to_string
std::string to_string(const digest_t &what)
Definition: sha1.hpp:398
restinio::utils::sha1::make_digest
digest_t make_digest(const std::uint8_t *what, std::size_t length)
Definition: sha1.hpp:417
restinio::websocket::basic::impl::write_message_details
raw_data_t write_message_details(const message_details_t &message)
Serialize websocket message details into bytes buffer.
Definition: ws_parser.hpp:515
restinio::websocket::basic::impl::ws_connection_handle_t
std::shared_ptr< ws_connection_base_t > ws_connection_handle_t
Alias for WebSocket connection handle.
Definition: ws_connection_base.hpp:65
restinio::websocket::basic::activation_t
activation_t
Flags for websocket activation policies.
Definition: websocket.hpp:199
restinio::websocket::basic::activation_t::immediate
@ immediate
Activate immediately after upgrade operation.
restinio::websocket::basic::activation_t::delayed
@ delayed
User will initiate activation later.
restinio::websocket::basic::opcode_t
opcode_t
Definition: message.hpp:37
restinio::websocket::basic::final_frame_flag_t
final_frame_flag_t
WS frame (message) "final"/"not final" flag.
Definition: message.hpp:133
restinio::websocket::basic::ws_handle_t
std::shared_ptr< ws_t > ws_handle_t
Alias for ws_t handle.
Definition: ws_connection_base.hpp:27
restinio
Definition: asio_include.hpp:21
restinio::writable_items_container_t
std::vector< writable_item_t > writable_items_container_t
Definition: buffers.hpp:705
restinio::http_connection_header_t::upgrade
@ upgrade
restinio::writable_item_type_t::trivial_write_operation
@ trivial_write_operation
Item is a buffer and must be written trivially.
restinio::field_to_string
const char * field_to_string(http_field_t f) noexcept
Helper sunction to get method string name.
Definition: http_headers.hpp:553
restinio::status_switching_protocols
http_status_line_t status_switching_protocols()
Definition: http_headers.hpp:2173
restinio::write_status_cb_t
std::function< void(const asio_ns::error_code &ec) > write_status_cb_t
An alias for a callback to be invoked after the write operation of a particular group of "buffers".
Definition: buffers.hpp:717
restinio::endpoint_t
asio_ns::ip::tcp::endpoint endpoint_t
An alias for endpoint type from Asio.
Definition: common_types.hpp:177
restinio::connection_id_t
std::uint64_t connection_id_t
Type for ID of connection.
Definition: common_types.hpp:174
std
STL namespace.
sha1.hpp
restinio::http_response_header_t
Resp header.
Definition: http_headers.hpp:2339
ws_connection.hpp
ws_connection_base.hpp
const
#define const
Definition: zconf.h:230