RESTinio

websocket.hpp
Go to the documentation of this file.
1/*
2 restinio
3*/
4
9#pragma once
10
11#include <functional>
12
13#include <restinio/websocket/message.hpp>
14#include <restinio/websocket/impl/ws_connection_base.hpp>
15#include <restinio/websocket/impl/ws_connection.hpp>
16#include <restinio/utils/base64.hpp>
17#include <restinio/utils/sha1.hpp>
18
19 namespace restinio
20{
21
22 namespace websocket
23{
24
25 namespace basic
26{
27
28//
29// ws_t
30//
31
33
38 class ws_t
39 : public std::enable_shared_from_this< ws_t >
40{
41 public:
42 //
43 // activate()
44 //
45
47 friend void activate( ws_t & ws )
48 {
49 ws.m_ws_connection_handle->init_read( ws.shared_from_this() );
50 }
51
52 ws_t( const ws_t & ) = delete;
53 ws_t( ws_t && ) = delete;
54 ws_t & operator = ( const ws_t & ) = delete;
55 ws_t & operator = ( ws_t && ) = delete;
56
57 ws_t(
58 impl::ws_connection_handle_t ws_connection_handle,
59 endpoint_t remote_endpoint )
60 : m_ws_connection_handle{ std::move( ws_connection_handle ) }
61 , m_remote_endpoint{ std::move( remote_endpoint ) }
62 {}
63
64 ~ws_t()
65 {
66 try
67 {
68 shutdown();
69 }
70 catch( ... )
71 {}
72 }
73
75
79 connection_id_t
80 connection_id() const
81 {
82 return m_ws_connection_handle ? m_ws_connection_handle->connection_id() : 0;
83 }
84
87 void
88 shutdown()
89 {
90 if( m_ws_connection_handle )
91 {
92 auto con = std::move( m_ws_connection_handle );
93 con->shutdown();
94 }
95 }
96
99 void
100 kill()
101 {
102 if( m_ws_connection_handle )
103 {
104 auto con = std::move( m_ws_connection_handle );
105 con->kill();
106 }
107 }
108
110 void
111 send_message(
112 final_frame_flag_t final_flag,
113 opcode_t opcode,
114 writable_item_t payload,
115 write_status_cb_t wscb = write_status_cb_t{} )
116 {
117 if( m_ws_connection_handle )
118 {
119 if( restinio::writable_item_type_t::trivial_write_operation ==
120 payload.write_type() )
121 {
122 writable_items_container_t bufs;
123 bufs.reserve( 2 );
124
125 // Create header serialize it and append to bufs .
126 impl::message_details_t details{
127 final_flag, opcode, asio_ns::buffer_size( payload.buf() ) };
128
129 bufs.emplace_back(
130 impl::write_message_details( details ) );
131
132 bufs.emplace_back( std::move( payload ) );
133
134 write_group_t wg{ std::move( bufs ) };
135
136 if( wscb )
137 {
138 wg.after_write_notificator( std::move( wscb ) );
139 }
140
141 // TODO: set flag.
142 const bool is_close_frame =
143 opcode_t::connection_close_frame == opcode;
144
145 if( is_close_frame )
146 {
147 auto con = std::move( m_ws_connection_handle );
148 con->write_data(
149 std::move( wg ),
150 is_close_frame );
151 }
152 else
153 {
154 m_ws_connection_handle->write_data(
155 std::move( wg ),
156 is_close_frame );
157 }
158 }
159 else
160 {
161 throw exception_t{ "ws doesn't support sendfile" };
162 }
163 }
164 else
165 {
166 throw exception_t{ "websocket is not available" };
167 }
168 }
169
170 void
171 send_message( message_t msg, write_status_cb_t wscb = write_status_cb_t{} )
172 {
173 send_message(
174 msg.final_flag(),
175 msg.opcode(),
176 writable_item_t{ std::move( msg.payload() ) },
177 std::move( wscb ) );
178 }
179
181 const endpoint_t & remote_endpoint() const noexcept { return m_remote_endpoint; }
182
183 private:
184 impl::ws_connection_handle_t m_ws_connection_handle;
185
187 const endpoint_t m_remote_endpoint;
188};
189
191 using ws_handle_t = std::shared_ptr< ws_t >;
192
193//
194// activation_t
195//
196
198 enum class activation_t
199{
201 immediate,
203 delayed
204};
205
206//
207// upgrade()
208//
209
211 template <
212 typename Traits,
213 typename WS_Message_Handler >
214 ws_handle_t
215 upgrade(
217 generic_request_type_from_traits_t<Traits> & req,
219 activation_t activation_flag,
221 http_header_fields_t upgrade_response_header_fields,
223 WS_Message_Handler ws_message_handler )
224{
225 // TODO: check if upgrade request?
226
228 if( !upgrade_response_header_fields.has_field( http_field::sec_websocket_accept ) )
229 {
230 throw exception_t{
231 fmt::format( "{} field is mandatory for upgrade response",
232 field_to_string( http_field::sec_websocket_accept ) ) };
233 }
234
235 if( !upgrade_response_header_fields.has_field( http_field::upgrade ) )
236 {
237 upgrade_response_header_fields.set_field( http_field::upgrade, "websocket" );
238 }
239
240 using connection_t = restinio::impl::connection_t< Traits >;
241 auto conn_ptr = std::move( restinio::impl::access_req_connection( req ) );
242 if( !conn_ptr )
243 {
244 throw exception_t{ "no connection for upgrade: already moved" };
245 }
246 auto & con = dynamic_cast< connection_t & >( *conn_ptr );
247
248 using ws_connection_t = impl::ws_connection_t< Traits, WS_Message_Handler >;
249
250 auto upgrade_internals = con.move_upgrade_internals();
251 auto ws_connection =
252 std::make_shared< ws_connection_t >(
253 con.connection_id(),
254 std::move( upgrade_internals.m_settings ),
255 std::move( upgrade_internals.m_socket ),
256 std::move( upgrade_internals.m_lifetime_monitor ),
257 std::move( ws_message_handler ) );
258
259 writable_items_container_t upgrade_response_bufs;
260 {
261 http_response_header_t upgrade_response_header{ status_switching_protocols() };
262 upgrade_response_header.swap_fields( upgrade_response_header_fields );
263 upgrade_response_header.connection( http_connection_header_t::upgrade );
264
265 const auto content_length_flag =
266 restinio::impl::content_length_field_presence_t::skip_content_length;
267
268 upgrade_response_bufs.emplace_back(
269 restinio::impl::create_header_string(
270 upgrade_response_header,
271 content_length_flag ) );
272 }
273
274 ws_connection->write_data(
275 write_group_t{ std::move( upgrade_response_bufs ) },
276 false );
277
278 auto result =
279 std::make_shared< ws_t >( std::move( ws_connection ), req.remote_endpoint() );
280
281 if( activation_t::immediate == activation_flag )
282 {
283 activate( *result );
284 }
285
286 // Returns strong handle on websocket, thus giving an ownership.
287 return result;
288}
289
290 template <
291 typename Traits,
292 typename WS_Message_Handler >
293 auto
294 upgrade(
295 generic_request_type_from_traits_t<Traits> & req,
296 activation_t activation_flag,
297 std::string sec_websocket_accept_field_value,
298 WS_Message_Handler ws_message_handler )
299{
300 http_header_fields_t upgrade_response_header_fields;
301 upgrade_response_header_fields.set_field(
302 http_field::sec_websocket_accept,
303 std::move( sec_websocket_accept_field_value ) );
304
305 return
306 upgrade< Traits, WS_Message_Handler >(
307 req,
308 activation_flag,
309 std::move( upgrade_response_header_fields ),
310 std::move( ws_message_handler ) );
311}
312
313 template <
314 typename Traits,
315 typename WS_Message_Handler >
316 auto
317 upgrade(
318 generic_request_type_from_traits_t<Traits> & req,
319 activation_t activation_flag,
320 std::string sec_websocket_accept_field_value,
321 std::string sec_websocket_protocol_field_value,
322 WS_Message_Handler ws_message_handler )
323{
324 http_header_fields_t upgrade_response_header_fields;
325 upgrade_response_header_fields.set_field(
326 http_field::sec_websocket_accept,
327 std::move( sec_websocket_accept_field_value ) );
328
329 upgrade_response_header_fields.set_field(
330 http_field::sec_websocket_protocol,
331 std::move( sec_websocket_protocol_field_value ) );
332
333 return
334 upgrade< Traits, WS_Message_Handler >(
335 req,
336 activation_flag,
337 std::move( upgrade_response_header_fields ),
338 std::move( ws_message_handler ) );
339}
340
341 template <
342 typename Traits,
343 typename WS_Message_Handler >
344 auto
345 upgrade(
346 generic_request_type_from_traits_t<Traits> & req,
347 activation_t activation_flag,
348 WS_Message_Handler ws_message_handler )
349{
350 const char * websocket_accept_field_suffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
351 const auto ws_key =
352 req.header().get_field( restinio::http_field::sec_websocket_key ) +
353 websocket_accept_field_suffix;
354
355 auto digest = restinio::utils::sha1::make_digest( ws_key );
356
357 std::string sec_websocket_accept_field_value = utils::base64::encode(
358 utils::sha1::to_string( digest ) );
359
360 http_header_fields_t upgrade_response_header_fields;
361 upgrade_response_header_fields.set_field(
362 http_field::sec_websocket_accept,
363 std::move( sec_websocket_accept_field_value ) );
364
365 return
366 upgrade< Traits, WS_Message_Handler >(
367 req,
368 activation_flag,
369 std::move( upgrade_response_header_fields ),
370 std::move( ws_message_handler ) );
371}
372
373} /* namespace basic */
374
375} /* namespace websocket */
376
377} /* 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:201
restinio::generic_request_t::header
const http_request_header_t & header() const noexcept
Get request header.
Definition: request_handler.hpp:256
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:288
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:496
restinio::writable_item_t::buf
asio_ns::const_buffer buf() const
Create a buf reference object used by ASIO.
Definition: buffers.hpp:594
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:582
restinio::write_group_t
Group of writable items transported to the context of underlying connection as one solid piece.
Definition: buffers.hpp:692
message.hpp
nonstd::optional_lite::std11::move
T & move(T &t)
Definition: optional.hpp:465
restinio::impl::access_req_connection
connection_handle_t & access_req_connection(generic_request_t< Extra_Data > &) noexcept
Definition: request_handler.hpp:477
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::upgrade
auto upgrade(generic_request_type_from_traits_t< Traits > &req, activation_t activation_flag, WS_Message_Handler ws_message_handler)
Definition: websocket.hpp:345
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::upgrade
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)
Upgrade http-connection of a current request to a websocket connection.
Definition: websocket.hpp:215
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:668
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:2172
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:680
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:2338
ws_connection.hpp
ws_connection_base.hpp
const
#define const
Definition: zconf.h:230