RESTinio
websocket.hpp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /*!
6  WebSocket messgage handler definition.
7 */
8 
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 
32 //! A WebSocket bind.
33 /*!
34  An abstraction for websocket. User have to keep this handle during all the period
35  that websocket is used. It must be stored in a `shared_ptr<ws_t>` (ws_handle_t)
36  and when the last reference on this handle is lost underlying connection will be closed.
37 */
38 class ws_t
39  : public std::enable_shared_from_this< ws_t >
40 {
41  public:
42  //
43  // activate()
44  //
45 
46  //! Activate websocket: start receiving messages.
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,
59  endpoint_t remote_endpoint )
62  {}
63 
64  ~ws_t()
65  {
66  try
67  {
68  shutdown();
69  }
70  catch( ... )
71  {}
72  }
73 
74  //! Get connection id.
75  /*!
76  If connection exists then its id is returned,
77  otherwise retursn zero.
78  */
80  connection_id() const
81  {
82  return m_ws_connection_handle ? m_ws_connection_handle->connection_id() : 0;
83  }
84 
85  //! Shutdown websocket: wait for all outgoing data to be sent,
86  //! and close connection.
87  void
89  {
90  if( m_ws_connection_handle )
91  {
92  auto con = std::move( m_ws_connection_handle );
93  con->shutdown();
94  }
95  }
96 
97  //! Kill websocket: close underlying tcp socket.
98  //! Do not tolerate unsent outgoing data.
99  void
101  {
102  if( m_ws_connection_handle )
103  {
104  auto con = std::move( m_ws_connection_handle );
105  con->kill();
106  }
107  }
108 
109  //! Send_websocket message.
110  void
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 
180  //! Get the remote endpoint of the underlying connection.
181  const endpoint_t & remote_endpoint() const noexcept { return m_remote_endpoint; }
182 
183  private:
185 
186  //! Remote endpoint for this ws-connection.
188 };
189 
190 //! Alias for ws_t handle.
191 using ws_handle_t = std::shared_ptr< ws_t >;
192 
193 //
194 // activation_t
195 //
196 
197 //! Flags for websocket activation policies.
198 enum class activation_t
199 {
200  //! Activate immediately after upgrade operation.
201  immediate,
202  //! User will initiate activation later.
203  delayed
204 };
205 
206 //
207 // upgrade()
208 //
209 
210 //! Upgrade http-connection of a current request to a websocket connection.
211 template <
212  typename Traits,
213  typename WS_Message_Handler >
216  //! Upgrade request.
218  //! Activation policy.
220  //! Response header fields.
222  //! Message handler.
224 {
225  // TODO: check if upgrade request?
226 
227  //! Check if mandatory field is available.
229  {
230  throw exception_t{
231  fmt::format(
233  "{} field is mandatory for upgrade response" ),
235  }
236 
238  {
240  }
241 
244  if( !conn_ptr )
245  {
246  throw exception_t{ "no connection for upgrade: already moved" };
247  }
248  auto & con = dynamic_cast< connection_t & >( *conn_ptr );
249 
251 
253  auto ws_connection =
255  con.connection_id(),
260 
262  {
266 
267  const auto content_length_flag =
269 
274  }
275 
278  false );
279 
280  auto result =
282 
284  {
285  activate( *result );
286  }
287 
288  // Returns strong handle on websocket, thus giving an ownership.
289  return result;
290 }
291 
292 template <
293  typename Traits,
294  typename WS_Message_Handler >
295 auto
301 {
306 
307  return
309  req,
313 }
314 
315 template <
316  typename Traits,
317  typename WS_Message_Handler >
318 auto
325 {
330 
334 
335  return
337  req,
341 }
342 
343 template <
344  typename Traits,
345  typename WS_Message_Handler >
346 auto
351 {
352  const char * websocket_accept_field_suffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
353  const auto ws_key =
356 
358 
360  utils::sha1::to_string( digest ) );
361 
366 
367  return
369  req,
373 }
374 
375 } /* namespace basic */
376 
377 } /* namespace websocket */
378 
379 } /* namespace restinio */
void shutdown()
Shutdown websocket: wait for all outgoing data to be sent, and close connection.
Definition: websocket.hpp:88
ws_t & operator=(const ws_t &)=delete
const endpoint_t & remote_endpoint() const noexcept
Get the remote endpoint of the underlying connection.
Definition: websocket.hpp:181
void send_message(message_t msg, write_status_cb_t wscb=write_status_cb_t{})
Definition: websocket.hpp:171
const endpoint_t m_remote_endpoint
Remote endpoint for this ws-connection.
Definition: websocket.hpp:187
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
User will initiate activation later.
connection_id_t connection_id() const
Get connection id.
Definition: websocket.hpp:80
std::enable_if< std::is_same< Parameter_Container, query_string_params_t >::value||std::is_same< Parameter_Container, router::route_params_t >::value, std::optional< Value_Type > >::type opt_value(const Parameter_Container &params, string_view_t key)
Gets the value of a parameter specified by key wrapped in std::optional<Value_Type> if parameter exis...
Definition: value_or.hpp:64
auto upgrade(generic_request_type_from_traits_t< Traits > &req, activation_t activation_flag, WS_Message_Handler ws_message_handler)
Definition: websocket.hpp:347
impl::ws_connection_handle_t m_ws_connection_handle
Definition: websocket.hpp:184
activation_t
Flags for websocket activation policies.
Definition: websocket.hpp:198
void kill()
Kill websocket: close underlying tcp socket. Do not tolerate unsent outgoing data.
Definition: websocket.hpp:100
ws_t & operator=(ws_t &&)=delete
friend void activate(ws_t &ws)
Activate websocket: start receiving messages.
Definition: websocket.hpp:47
Activate immediately after upgrade operation.
ws_t(impl::ws_connection_handle_t ws_connection_handle, endpoint_t remote_endpoint)
Definition: websocket.hpp:57