RESTinio
sendfile_operation_win.ipp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
9 #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
10 
11 #include <cstdio>
12 
14 
15 namespace restinio
16 {
17 
18 namespace impl
19 {
20 
21 namespace asio_details
22 {
23 
24 #if RESTINIO_ASIO_VERSION < 101300
25 
26 template<typename Socket >
27 decltype(auto)
28 executor_or_context_from_socket( Socket & socket )
29 {
30  return socket.get_executor().context();
31 }
32 
33 #else
34 
35 template<typename Socket >
36 decltype(auto)
37 executor_or_context_from_socket( Socket & socket )
38 {
39  return socket.get_executor();
40 }
41 
42 #endif
43 
44 } /* namespace asio_details */
45 
46 //
47 // sendfile_operation_runner_t
48 //
49 
51 template < typename Socket >
52 class sendfile_operation_runner_t final
53  : public sendfile_operation_runner_base_t< Socket >
54 {
55  public:
56  using base_type_t = sendfile_operation_runner_base_t< Socket >;
57 
62 
64  sendfile_t & sf,
65  default_asio_executor executor,
66  Socket & socket,
67  after_sendfile_cb_t after_sendfile_cb )
68  : base_type_t{ sf, std::move( executor), socket, std::move( after_sendfile_cb ) }
69  {
70  // We have passed sf.file_descriptor() to m_file_handle object.
71  // It means that file description will be closed automatically
72  // by m_file_handle.
73  // But sf still holds the same file_descriptor. Because of that
74  // we should tell sf to release this file_descriptor.
75  takeaway_file_descriptor(sf).release();
76  }
77 
78  virtual void
79  start() override
80  {
81  init_next_read_some_from_file();
82  }
83 
88  void
89  init_next_read_some_from_file() noexcept
90  {
91  const auto desired_size =
92  std::min< file_size_t >( this->m_remained_size, this->m_chunk_size );
93 
94  try
95  {
96  this->m_file_handle.async_read_some_at(
97  this->m_next_write_offset,
98  asio_ns::buffer(
99  this->m_buffer.get(),
100  static_cast< std::size_t >( desired_size ) ),
101  asio_ns::bind_executor(
102  this->m_executor,
103  make_async_read_some_at_handler() ) );
104  }
105  catch( ... )
106  {
107  this->m_after_sendfile_cb(
110  this->m_transfered_size );
111  }
112  }
113 
118  void
119  init_next_write( std::size_t len ) noexcept
120  {
121  try
122  {
123  asio_ns::async_write(
124  this->m_socket,
126  this->m_buffer.get(),
127  static_cast< std::size_t >( len ) },
128  asio_ns::bind_executor(
129  this->m_executor,
131  }
132  catch( ... )
133  {
134  this->m_after_sendfile_cb(
136  asio_convertible_error_t::async_write_call_failed ),
137  this->m_transfered_size );
138  }
139  }
140 
141  private:
142  std::unique_ptr< char[] > m_buffer{ new char [ this->m_chunk_size ] };
143  asio_ns::windows::random_access_handle m_file_handle{
144  asio_details::executor_or_context_from_socket(this->m_socket),
145  this->m_file_descriptor
146  };
147 
148  auto
149  make_async_read_some_at_handler() noexcept
150  {
151  return [this, ctx = this->shared_from_this()]
152  // NOTE: this lambda is noexcept since v.0.6.0.
153  ( const asio_ns::error_code & ec, std::size_t len ) noexcept
154  {
155  if( ec || 0 == this->m_remained_size )
156  {
157  this->m_after_sendfile_cb( ec, this->m_transfered_size );
158  }
159  if( !ec )
160  {
161  if( 0 != len )
162  init_next_write( len );
163  else
164  {
165  this->m_after_sendfile_cb(
167  this->m_transfered_size );
168  }
169  }
170  else
171  {
172  this->m_after_sendfile_cb( ec, this->m_transfered_size );
173  }
174  };
175  }
176 
177  auto
178  make_async_write_handler() noexcept
179  {
180  return [ this, ctx = this->shared_from_this() ]
181  // NOTE: this lambda is noexcept since v.0.6.0.
182  ( const asio_ns::error_code & ec, std::size_t written ) noexcept
183  {
184  if( !ec )
185  {
186  this->m_remained_size -= written;
187  this->m_transfered_size += written;
188  this->m_next_write_offset += written;
189 
190  if( 0 == this->m_remained_size )
191  {
192  this->m_after_sendfile_cb( ec, this->m_transfered_size );
193  }
194  else
195  {
196  this->init_next_read_some_from_file();
197  }
198  }
199  else
200  {
201  this->m_after_sendfile_cb( ec, this->m_transfered_size );
202  }
203  };
204  }
205 };
206 
208 template <>
209 class sendfile_operation_runner_t < asio_ns::ip::tcp::socket > final
210  : public sendfile_operation_runner_base_t< asio_ns::ip::tcp::socket >
211 {
212  auto
213  make_completion_handler() noexcept
214  {
215  return [this, ctx = shared_from_this() ]
216  // NOTE: this lambda is noexcept since v.0.6.0.
217  ( const asio_ns::error_code & ec, std::size_t written )
218  {
219  if( !ec )
220  {
221  m_remained_size -= written;
222  m_transfered_size += written;
223  m_next_write_offset += written;
224 
225  if( 0 == m_remained_size )
226  {
228  }
229  else
230  {
231  init_next_write();
232  }
233  }
234  else
235  {
237  }
238  };
239  }
240 
241  public:
243 
248 
250  sendfile_t & sf,
251  default_asio_executor executor,
252  asio_ns::ip::tcp::socket & socket,
253  after_sendfile_cb_t after_sendfile_cb )
254  : base_type_t{ sf, std::move( executor), socket, std::move( after_sendfile_cb ) }
255  {
256  // We have passed sf.file_descriptor() to m_file_handle object.
257  // It means that file description will be closed automatically
258  // by m_file_handle.
259  // But sf still holds the same file_descriptor. Because of that
260  // we should tell sf to release this file_descriptor.
261  takeaway_file_descriptor(sf).release();
262  }
263 
264  virtual void
265  start() override
266  {
267  init_next_write();
268  }
269 
274  void
275  init_next_write() noexcept
276  {
277  // In this function bind_executor is the main suspect
278  // for throwing an exception. Because of that the whole
279  // function's logic is wrapped by try-catch.
280  try
281  {
282  asio_ns::windows::overlapped_ptr overlapped{
283  asio_details::executor_or_context_from_socket( m_socket ),
284  asio_ns::bind_executor(
285  m_executor,
286  make_completion_handler() )
287  };
288 
289  // Set offset.
290  overlapped.get()->Offset =
291  static_cast< DWORD >( m_next_write_offset & 0xFFFFFFFFULL );
292  overlapped.get()->OffsetHigh =
293  static_cast< DWORD >( (m_next_write_offset>>32) & 0xFFFFFFFFULL );
294 
295  // Amount of data to transfer.
296  const auto desired_size =
297  std::min< file_size_t >( this->m_remained_size, this->m_chunk_size );
298 
299  // Initiate the TransmitFile operation.
300  BOOL ok =
301  ::TransmitFile(
302  m_socket.native_handle(),
303  m_file_handle.native_handle(),
304  static_cast< DWORD >( desired_size ),
305  0,
306  overlapped.get(),
307  nullptr,
308  0 );
309 
310  DWORD last_error = ::GetLastError();
311 
312  // Check if the operation completed immediately.
313  if( !ok && last_error != ERROR_IO_PENDING )
314  {
315  // The operation completed immediately, so a completion notification needs
316  // to be posted. When complete() is called, ownership of the OVERLAPPED-
317  // derived object passes to the io_context.
318  overlapped.complete( make_error_code( last_error ) , 0 );
319  }
320  else
321  {
322  // The operation was successfully initiated, so ownership of the
323  // OVERLAPPED-derived object has passed to the io_context.
324  overlapped.release();
325  }
326  }
327  catch( ... )
328  {
329  // Report that error as a failure of async_write.
330  this->m_after_sendfile_cb(
332  asio_convertible_error_t::async_write_call_failed ),
333  this->m_transfered_size );
334  }
335  }
336 
337  private:
338  std::unique_ptr< char[] > m_buffer{
339  std::make_unique< char[] >(
341  m_chunk_size ) )
342  };
343  asio_ns::windows::random_access_handle m_file_handle{
344  asio_details::executor_or_context_from_socket(m_socket),
346  };
347 };
348 
349 } /* namespace impl */
350 
351 } /* namespace restinio */
352 
353 #else // #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
354 
356 
357 #endif // #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
restinio::const_buffer
constexpr const_buffer_t const_buffer(const void *str, std::size_t size) noexcept
Definition: buffers.hpp:424
restinio::impl::sendfile_operation_runner_base_t::m_socket
Socket & m_socket
Definition: sendfile_operation.hpp:82
restinio::make_asio_compaible_error
asio_ns::error_code make_asio_compaible_error(asio_convertible_error_t err) noexcept
Make restinio error_code compatible with asio_ns::error_code.
Definition: asio_include.hpp:210
nonstd::optional_lite::std11::move
T & move(T &t)
Definition: optional.hpp:421
restinio::impl::sendfile_operation_runner_base_t::m_remained_size
file_size_t m_remained_size
Definition: sendfile_operation.hpp:74
restinio::impl::sendfile_operation_runner_t::base_type_t
sendfile_operation_runner_base_t< Socket > base_type_t
Definition: sendfile_operation_default.ipp:27
restinio::impl::sendfile_operation_runner_base_t::m_file_descriptor
file_descriptor_t m_file_descriptor
Definition: sendfile_operation.hpp:72
restinio::impl::sendfile_operation_runner_t::sendfile_operation_runner_t
sendfile_operation_runner_t(const sendfile_operation_runner_t &)=delete
restinio::asio_convertible_error_t::write_was_not_executed
@ write_was_not_executed
After write notificator error: data was not sent, connection closed (or aborted) before a given piece...
restinio::impl::sendfile_operation_runner_base_t< asio_ns::ip::tcp::socket >
restinio::impl::sendfile_operation_runner_t::init_next_write
void init_next_write() noexcept
Definition: sendfile_operation_default.ipp:65
restinio::asio_ec::eof
constexpr auto eof
Definition: asio_include.hpp:41
sendfile_operation_default.ipp
restinio::default_asio_executor
asio_ns::executor default_asio_executor
Definition: asio_include.hpp:224
restinio::impl::sendfile_operation_runner_base_t::m_after_sendfile_cb
after_sendfile_cb_t m_after_sendfile_cb
Definition: sendfile_operation.hpp:83
restinio::utils::impl::uint64_to_size_t
std::size_t uint64_to_size_t(std::uint64_t v)
Helper function for truncating uint64 to std::size_t with exception if that truncation will lead to d...
Definition: safe_uint_truncate.hpp:59
restinio::impl::sendfile_operation_runner_base_t::m_next_write_offset
file_offset_t m_next_write_offset
Definition: sendfile_operation.hpp:73
restinio::impl::sendfile_operation_runner_base_t::m_transfered_size
file_size_t m_transfered_size
Definition: sendfile_operation.hpp:75
restinio::impl::sendfile_operation_runner_t::m_buffer
std::unique_ptr< char[] > m_buffer
Definition: sendfile_operation_default.ipp:108
restinio
Definition: asio_include.hpp:21
restinio::impl::sendfile_operation_runner_base_t::m_executor
default_asio_executor m_executor
Definition: sendfile_operation.hpp:81
restinio::impl::sendfile_operation_runner_t::start
virtual void start() override
Definition: sendfile_operation_default.ipp:39
restinio::impl::after_sendfile_cb_t
std::function< void(const asio_ns::error_code &, file_size_t) > after_sendfile_cb_t
Callback type for invocation when sendfile operation completes.
Definition: sendfile_operation.hpp:40
restinio::impl::make_error_code
auto make_error_code(const Error_Type &e) noexcept
Definition: sendfile_operation.hpp:88
restinio::impl::sendfile_operation_runner_t::make_async_write_handler
auto make_async_write_handler() noexcept
Helper method for making a lambda for async_write completion handler.
Definition: sendfile_operation_default.ipp:112
safe_uint_truncate.hpp
Helpers for safe truncation of unsigned integers.
restinio::impl::sendfile_operation_runner_base_t::m_chunk_size
const file_size_t m_chunk_size
Definition: sendfile_operation.hpp:77
restinio::impl::sendfile_operation_runner_t::operator=
sendfile_operation_runner_t & operator=(const sendfile_operation_runner_t &)=delete
restinio::status_code::ok
constexpr http_status_code_t ok
Definition: http_headers.hpp:2043