RESTinio
tls.hpp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /*!
6  Support for https.
7 */
8 
9 #pragma once
10 
11 #include <restinio/traits.hpp>
12 #include <restinio/impl/tls_socket.hpp>
13 
14 namespace restinio
15 {
16 
17 namespace connection_state
18 {
19 
20 /*!
21  * @brief Accessor to TLS-specific information related to a connection.
22  *
23  * @note
24  * You have to manually include `restinio/tls.hpp` to get the definition
25  * of that class. This definition is not present if you include only
26  * `restinio/all.hpp`
27  *
28  * @since v.0.6.0
29  */
31 {
33 
34 public:
35  tls_accessor_t( tls_socket_t & tls_socket ) : m_tls_socket{tls_socket} {}
36 
37  /*!
38  * @brief Get the access to native handle behind Asio's ssl_stream.
39  *
40  * Usage example:
41  * \code
42  * struct openssl_free_t {
43  * void operator()(void * ptr) const noexcept
44  * {
45  * OPENSSL_free( ptr );
46  * }
47  * };
48  *
49  * std::string extract_user_name_from_client_certificate(
50  * const restinio::connection_state::tls_accessor_t & info )
51  * {
52  * auto nhandle = info.native_handle();
53  *
54  * std::unique_ptr<X509, decltype(&X509_free)> client_cert{
55  * SSL_get_peer_certificate(nhandle),
56  * X509_free
57  * };
58  * if( !client_cert )
59  * throw std::runtime_error( "Unable to get client certificate!" );
60  *
61  * X509_NAME * subject_name = X509_get_subject_name( client_cert.get() );
62  *
63  * int last_pos = -1;
64  * last_pos = X509_NAME_get_index_by_NID(
65  * subject_name,
66  * NID_commonName,
67  * last_pos );
68  * if( last_pos < 0 )
69  * throw std::runtime_error( "commonName is not found!" );
70  *
71  * unsigned char * common_name_utf8{};
72  * if( ASN1_STRING_to_UTF8(
73  * &common_name_utf8,
74  * X509_NAME_ENTRY_get_data(
75  * X509_NAME_get_entry( subject_name, last_pos ) ) ) < 0 )
76  * throw std::runtime_error( "ASN1_STRING_to_UTF8 failed!" );
77  *
78  * std::unique_ptr<unsigned char, openssl_free_t > common_name_deleter{
79  * common_name_utf8
80  * };
81  *
82  * return { reinterpret_cast<char *>(common_name_utf8) };
83  * }
84  * \endcode
85  *
86  * @since v.0.6.0
87  */
89  auto native_handle() const noexcept
90  {
92  }
93 };
94 
95 //
96 // The implementation of TLS-related part of notice_t.
97 //
98 
99 template< typename Lambda >
100 void
102 {
103  if( m_tls_socket )
105 }
106 
107 template< typename Lambda >
108 decltype(auto)
110 {
111  if( !m_tls_socket )
112  throw exception_t{ "an attempt to call inspect_tls for "
113  "non-TLS-connection" };
114 
115  return lambda( tls_accessor_t{*m_tls_socket} );
116 }
117 
118 template< typename Lambda, typename T >
119 T
121 {
122  if( m_tls_socket )
123  return lambda( tls_accessor_t{*m_tls_socket} );
124 
125  return default_value;
126 }
127 
128 } /* namespace connection_state */
129 
130 //
131 // tls_traits_t
132 //
133 
134 template <
135  typename Timer_Factory,
136  typename Logger,
138  typename Strand = asio_ns::strand< default_asio_executor > >
140 
141 //
142 // single_thread_traits_t
143 //
144 
145 template <
146  typename Timer_Factory,
147  typename Logger,
151 
153 
154 //
155 // prepare_connection_and_start_read()
156 //
157 
158 //! Customizes connection init routine with an additional step:
159 //! perform handshake and only then start reading.
160 template < typename Connection, typename Start_Read_CB, typename Failed_CB >
161 void
163  tls_socket_t & socket,
164  Connection & con,
165  Start_Read_CB start_read_cb,
166  Failed_CB failed_cb )
167 {
171  failed_cb = std::move( failed_cb ),
172  con = con.shared_from_this() ]( const asio_ns::error_code & ec ){
173  if( !ec )
174  start_read_cb();
175  else
176  failed_cb( ec );
177  } );
178 }
179 
180 //
181 // socket_type_dependent_settings_t
182 //
183 
184 //! Customizes extra settings needed for working with socket.
185 /*!
186  Adds tls context setting.
187 */
188 template < typename Settings >
189 class socket_type_dependent_settings_t< Settings, tls_socket_t >
190 {
191 protected:
193 
194 public:
197  socket_type_dependent_settings_t && ) = default;
198 
199  //! Setup an exclusive TLS-context for server's settings.
200  Settings &
202  asio_ns::ssl::context context ) &
203  {
204  m_tls_context = std::make_shared< asio_ns::ssl::context >(
205  std::move( context ) );
206  return upcast_reference();
207  }
208 
209  //! Setup an exclusive TLS-context for server's settings.
210  Settings &&
212  asio_ns::ssl::context context ) &&
213  {
214  return std::move( this->tls_context( std::move( context ) ) );
215  }
216 
217  //! Setup a shared TLS-context for server's settings.
218  /*!
219  * This method can be used when several servers should share
220  * the same TLS context. Or if TLS should be shared with some
221  * other entity in an application.
222  *
223  * Example:
224  * @code
225  * using traits_t = restinio::default_tls_traits_t;
226  *
227  * auto tls_context = std::make_shared< asio::ssl::context >(
228  * asio::ssl::context::sslv23 );
229  * ... // Tuning of tls_context.
230  *
231  * restinio::server_settings_t< traits_t > first_settings;
232  * first_settings.address( "localhost" );
233  * first_settings.port( 443 );
234  * first_settings.tls_context( tls_context );
235  * ...
236  *
237  * restinio::server_settings_t< traits_t > second_settings;
238  * second_settings.address( "localhost" );
239  * second_settings.port( 5553 );
240  * second_settings.tls_context( tls_context );
241  * ...
242  * @endcode
243  *
244  * @since v.0.6.10
245  */
246  Settings &
248  std::shared_ptr< asio_ns::ssl::context > shared_context ) &
249  {
250  m_tls_context = std::move( shared_context );
251  return upcast_reference();
252  }
253 
254  //! Setup a shared TLS-context for server's settings.
255  /*!
256  * This method can be used when several servers should share
257  * the same TLS context. Or if TLS should be shared with some
258  * other entity in an application.
259  *
260  * Example:
261  * @code
262  * using traits_t = restinio::default_tls_traits_t;
263  *
264  * auto tls_context = std::make_shared< asio::ssl::context >(
265  * asio::ssl::context::sslv23 );
266  * ... // Tuning of tls_context.
267  *
268  * auto first_server = restinio::run_async< traits_t >(
269  * restinio::own_io_context(),
270  * restinio::server_settings_t< traits_t >{}
271  * .address( "localhost" )
272  * .port( 443 )
273  * .tls_context( tls_context ),
274  * 4u );
275  *
276  * auto second_server = restinio::run_async< traits_t >(
277  * restinio::own_io_context(),
278  * restinio::server_settings_t< traits_t >{}
279  * .address( "localhost" )
280  * .port( 5553 )
281  * .tls_context( tls_context ),
282  * 4u );
283  * @endcode
284  *
285  * @since v.0.6.10
286  */
287  Settings &&
289  std::shared_ptr< asio_ns::ssl::context > shared_context ) &&
290  {
291  return std::move( this->tls_context( std::move(shared_context) ) );
292  }
293 
294  //FIXME: should be removed in v.0.7.
295  /*!
296  * @deprecated
297  * This method is going to be removed in v.0.7.
298  * giveaway_tls_context() should be used instead.
299  */
300  [[deprecated]]
303  {
304  asio_ns::ssl::context result{ std::move( *m_tls_context ) };
305  m_tls_context.reset();
306 
307  return result;
308  }
309 
310  //! Get away the TLS-context from settings.
311  /*!
312  * @note
313  * This method is intended to be used by RESTinio's internals.
314  *
315  * @since v.0.6.10
316  */
319  {
320  return std::move(m_tls_context);
321  }
322 
323  private:
324  Settings &
326  {
327  return static_cast< Settings & >( *this );
328  }
329 
331  std::make_shared< asio_ns::ssl::context >(
332  asio_ns::ssl::context::sslv23 )
333  };
334 };
335 
336 namespace impl
337 {
338 
339 // An overload for the case of non-TLS-connection.
340 inline tls_socket_t *
342  tls_socket_t & socket ) noexcept
343 {
344  return &socket;
345 }
346 
347 //
348 // socket_supplier_t
349 //
350 
351 //! A custom socket storage for tls_socket_t.
352 template <>
353 class socket_supplier_t< tls_socket_t >
354 {
355  protected:
356  template < typename Settings >
358  Settings & settings,
359  asio_ns::io_context & io_context )
360  : m_tls_context{ settings.giveaway_tls_context() }
361  , m_io_context{ io_context }
362  {
363  m_sockets.reserve( settings.concurrent_accepts_count() );
364 
365  while( m_sockets.size() < settings.concurrent_accepts_count() )
366  {
367  m_sockets.emplace_back( m_io_context, m_tls_context );
368  }
369  }
370 
371  virtual ~socket_supplier_t() = default;
372 
373  tls_socket_t &
375  //! Index of a socket in the pool.
376  std::size_t idx )
377  {
378  return m_sockets.at( idx );
379  }
380 
381  auto
383  //! Index of a socket in the pool.
384  std::size_t idx )
385  {
386  tls_socket_t res{ m_io_context, m_tls_context };
387  std::swap( res, m_sockets.at( idx ) );
388  return res;
389  }
390 
391  //! The number of sockets that can be used for
392  //! cuncurrent accept operations.
393  auto
395  {
396  return m_sockets.size();
397  }
398 
399  private:
403 };
404 
405 } /* namespace impl */
406 
407 } /* namespace restinio */
std::shared_ptr< asio_ns::ssl::context > m_tls_context
Definition: tls.hpp:330
std::shared_ptr< asio_ns::ssl::context > giveaway_tls_context()
Get away the TLS-context from settings.
Definition: tls.hpp:318
tls_socket_t & socket(std::size_t idx)
Definition: tls.hpp:374
tls_socket_t * make_tls_socket_pointer_for_state_listener(tls_socket_t &socket) noexcept
Definition: tls.hpp:341
RESTINIO_NODISCARD auto native_handle() const noexcept
Get the access to native handle behind Asio&#39;s ssl_stream.
Definition: tls.hpp:89
Settings & tls_context(std::shared_ptr< asio_ns::ssl::context > shared_context) &
Setup a shared TLS-context for server&#39;s settings.
Definition: tls.hpp:247
Settings && tls_context(std::shared_ptr< asio_ns::ssl::context > shared_context) &&
Setup a shared TLS-context for server&#39;s settings.
Definition: tls.hpp:288
socket_type_dependent_settings_t(socket_type_dependent_settings_t &&)=default
void prepare_connection_and_start_read(tls_socket_t &socket, Connection &con, Start_Read_CB start_read_cb, Failed_CB failed_cb)
Customizes connection init routine with an additional step: perform handshake and only then start rea...
Definition: tls.hpp:162
socket_supplier_t(Settings &settings, asio_ns::io_context &io_context)
Definition: tls.hpp:357
tls_accessor_t(tls_socket_t &tls_socket)
Definition: tls.hpp:35
auto concurrent_accept_sockets_count() const
The number of sockets that can be used for cuncurrent accept operations.
Definition: tls.hpp:394
std::vector< tls_socket_t > m_sockets
Definition: tls.hpp:402
std::shared_ptr< asio_ns::ssl::context > m_tls_context
Definition: tls.hpp:400
RESTINIO_NODISCARD char to_lower_case(unsigned char ch)
std::enable_if< std::is_same< Parameter_Container, query_string_params_t >::value||std::is_same< Parameter_Container, router::route_params_t >::value, optional_t< Value_Type > >::type opt_value(const Parameter_Container &params, string_view_t key)
Gets the value of a parameter specified by key wrapped in optional_t<Value_Type> if parameter exists ...
Definition: value_or.hpp:64