RESTinio
websocket.hpp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
9 #pragma once
10 
11 #include <functional>
12 
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 
58  impl::ws_connection_handle_t ws_connection_handle,
60  : m_ws_connection_handle{ std::move( ws_connection_handle ) }
62  {}
63 
65  {
66  try
67  {
68  shutdown();
69  }
70  catch( ... )
71  {}
72  }
73 
75 
80  connection_id() const
81  {
82  return m_ws_connection_handle ? m_ws_connection_handle->connection_id() : 0;
83  }
84 
87  void
89  {
91  {
92  auto con = std::move( m_ws_connection_handle );
93  con->shutdown();
94  }
95  }
96 
99  void
101  {
103  {
104  auto con = std::move( m_ws_connection_handle );
105  con->kill();
106  }
107  }
108 
110  void
112  final_frame_flag_t final_flag,
113  opcode_t opcode,
114  writable_item_t payload,
116  {
118  {
119  if( restinio::writable_item_type_t::trivial_write_operation ==
120  payload.write_type() )
121  {
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
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:
185 
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 >
217  request_t & 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 
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 =
267 
268  upgrade_response_bufs.emplace_back(
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
295  request_t & 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
318  request_t & 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
346  request_t & 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 */
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::exception_t
Exception class for all exceptions thrown by RESTinio.
Definition: exception.hpp:26
restinio::websocket::basic::ws_t::ws_t
ws_t(const ws_t &)=delete
restinio::websocket::basic::ws_t::kill
void kill()
Kill websocket: close underlying tcp socket. Do not tolerate unsent outgoing data.
Definition: websocket.hpp:100
nonstd::optional_lite::std11::move
T & move(T &t)
Definition: optional.hpp:421
restinio::utils::sha1::to_string
std::string to_string(const digest_t &what)
Definition: sha1.hpp:398
restinio::endpoint_t
asio_ns::ip::tcp::endpoint endpoint_t
An alias for endpoint type from Asio.
Definition: common_types.hpp:158
message.hpp
restinio::websocket::basic::activation_t
activation_t
Flags for websocket activation policies.
Definition: websocket.hpp:199
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::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::websocket::basic::ws_t::activate
friend void activate(ws_t &ws)
Activate websocket: start receiving messages.
Definition: websocket.hpp:47
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::websocket::basic::message_t::final_flag
final_frame_flag_t final_flag() const noexcept
Get final flag.
Definition: message.hpp:168
restinio::websocket::basic::ws_t::m_ws_connection_handle
impl::ws_connection_handle_t m_ws_connection_handle
Definition: websocket.hpp:184
restinio::connection_id_t
std::uint64_t connection_id_t
Type for ID of connection.
Definition: common_types.hpp:155
restinio::request_t::remote_endpoint
const endpoint_t & remote_endpoint() const noexcept
Get the remote endpoint of the underlying connection.
Definition: request_handler.hpp:124
restinio::websocket::basic::upgrade
ws_handle_t upgrade(request_t &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::ws_t::~ws_t
~ws_t()
Definition: websocket.hpp:64
restinio::websocket::basic::impl::message_details_t
Websocket message class with more detailed protocol information.
Definition: ws_parser.hpp:63
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::impl::content_length_field_presence_t::add_content_length
@ add_content_length
ws_connection.hpp
restinio::websocket::basic::impl::ws_connection_t
Context for handling websocket connections.
Definition: ws_connection.hpp:125
restinio::websocket::basic::ws_t::ws_t
ws_t(ws_t &&)=delete
restinio::websocket::basic::opcode_t
opcode_t
Definition: message.hpp:37
restinio::http_header_fields_t
Header fields map.
Definition: http_headers.hpp:703
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::utils::sha1::make_digest
digest_t make_digest(const std::uint8_t *what, std::size_t length)
Definition: sha1.hpp:417
restinio::websocket::basic::upgrade
auto upgrade(request_t &req, activation_t activation_flag, WS_Message_Handler ws_message_handler)
Definition: websocket.hpp:345
restinio::websocket::basic::message_t
WebSocket message.
Definition: message.hpp:145
restinio::utils::base64::encode
std::string encode(string_view_t str)
Definition: base64.hpp:123
sha1.hpp
restinio::http_connection_header_t::keep_alive
@ keep_alive
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::writable_item_t::buf
asio_ns::const_buffer buf() const
Create a buf reference object used by ASIO.
Definition: buffers.hpp:594
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::ws_t::remote_endpoint
const endpoint_t & remote_endpoint() const noexcept
Get the remote endpoint of the underlying connection.
Definition: websocket.hpp:181
restinio::request_t::header
const http_request_header_t & header() const noexcept
Get request header.
Definition: request_handler.hpp:92
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::status_switching_protocols
http_status_line_t status_switching_protocols()
Definition: http_headers.hpp:2172
restinio::write_group_t
Group of writable items transported to the context of underlying connection as one solid piece.
Definition: buffers.hpp:692
restinio
Definition: asio_include.hpp:21
restinio::writable_item_t
Class for storing the buffers used for streaming body (request/response).
Definition: buffers.hpp:496
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::ws_t::connection_id
connection_id_t connection_id() const
Get connection id.
Definition: websocket.hpp:80
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
base64.hpp
restinio::websocket::basic::ws_t
A WebSocket bind.
Definition: websocket.hpp:40
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
ws_connection_base.hpp
restinio::impl::access_req_connection
connection_handle_t & access_req_connection(request_t &) noexcept
Definition: request_handler.hpp:195
restinio::websocket::basic::final_frame_flag_t
final_frame_flag_t
WS frame (message) "final"/"not final" flag.
Definition: message.hpp:133
restinio::http_response_header_t
Resp header.
Definition: http_headers.hpp:2338
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::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::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::websocket::basic::ws_t::operator=
ws_t & operator=(const ws_t &)=delete
restinio::writable_items_container_t
std::vector< writable_item_t > writable_items_container_t
Definition: buffers.hpp:668
restinio::impl::connection_t
Context for handling http connections.
Definition: connection.hpp:306
restinio::websocket::basic::activation_t::immediate
@ immediate
Activate immediately after upgrade operation.
restinio::request_t
HTTP Request data.
Definition: request_handler.hpp:44
restinio::websocket::basic::message_t::opcode
opcode_t opcode() const noexcept
Definition: message.hpp:186
const
#define const
Definition: zconf.h:230