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
13#include <restinio/utils/impl/safe_uint_truncate.hpp>
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)
28executor_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)
37executor_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
58 sendfile_operation_runner_t( const sendfile_operation_runner_t & ) = delete;
59 sendfile_operation_runner_t( sendfile_operation_runner_t && ) = delete;
60 sendfile_operation_runner_t & operator = ( const sendfile_operation_runner_t & ) = delete;
61 sendfile_operation_runner_t & operator = ( sendfile_operation_runner_t && ) = delete;
62
63 sendfile_operation_runner_t(
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(
108 make_asio_compaible_error(
109 asio_convertible_error_t::async_read_some_at_call_failed ),
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,
125 asio_ns::const_buffer{
126 this->m_buffer.get(),
127 static_cast< std::size_t >( len ) },
128 asio_ns::bind_executor(
129 this->m_executor,
130 make_async_write_handler() ) );
131 }
132 catch( ... )
133 {
134 this->m_after_sendfile_cb(
135 make_asio_compaible_error(
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(
166 make_error_code( asio_ec::eof ),
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 {
227 m_after_sendfile_cb( ec, m_transfered_size );
228 }
229 else
230 {
231 init_next_write();
232 }
233 }
234 else
235 {
236 m_after_sendfile_cb( ec, m_transfered_size );
237 }
238 };
239 }
240
241 public:
242 using base_type_t = sendfile_operation_runner_base_t< asio_ns::ip::tcp::socket >;
243
244 sendfile_operation_runner_t( const sendfile_operation_runner_t & ) = delete;
245 sendfile_operation_runner_t( sendfile_operation_runner_t && ) = delete;
246 sendfile_operation_runner_t & operator = ( const sendfile_operation_runner_t & ) = delete;
247 sendfile_operation_runner_t & operator = ( sendfile_operation_runner_t && ) = delete;
248
249 sendfile_operation_runner_t(
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(
331 make_asio_compaible_error(
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[] >(
340 ::restinio::utils::impl::uint64_to_size_t(
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),
345 m_file_descriptor
346 };
347};
348
349} /* namespace impl */
350
351} /* namespace restinio */
352
353#else // #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
354
355#include "sendfile_operation_default.ipp"
356
357#endif // #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
restinio::impl::sendfile_operation_runner_base_t< asio_ns::ip::tcp::socket >
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::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_base_t::m_remained_size
file_size_t m_remained_size
Definition: sendfile_operation.hpp:74
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_file_descriptor
file_descriptor_t m_file_descriptor
Definition: sendfile_operation.hpp:72
restinio::impl::sendfile_operation_runner_base_t::m_executor
default_asio_executor m_executor
Definition: sendfile_operation.hpp:81
restinio::impl::sendfile_operation_runner_base_t::m_socket
Socket & m_socket
Definition: sendfile_operation.hpp:82
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::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
restinio::impl::sendfile_operation_runner_t::start
virtual void start() override
Definition: sendfile_operation_default.ipp:39
restinio::impl::sendfile_operation_runner_t::sendfile_operation_runner_t
sendfile_operation_runner_t(const sendfile_operation_runner_t &)=delete
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_t::operator=
sendfile_operation_runner_t & operator=(const sendfile_operation_runner_t &)=delete
restinio::impl::sendfile_operation_runner_t::init_next_write
void init_next_write() noexcept
Definition: sendfile_operation_default.ipp:65
nonstd::optional_lite::std11::move
T & move(T &t)
Definition: optional.hpp:465
restinio::asio_ec::eof
constexpr auto eof
Definition: asio_include.hpp:41
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::status_code::ok
constexpr http_status_code_t ok
Definition: http_headers.hpp:2043
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
Definition: asio_include.hpp:21
restinio::default_asio_executor
asio_ns::executor default_asio_executor
Definition: asio_include.hpp:224
restinio::asio_convertible_error_t::async_read_some_at_call_failed
@ async_read_some_at_call_failed
A call to async_read_some_at failed. The corresponding sendfile operation wasn't done.
restinio::asio_convertible_error_t::async_write_call_failed
@ async_write_call_failed
A call to async_write failed. The corresponding write operation wasn't done.
restinio::const_buffer
constexpr const_buffer_t const_buffer(const void *str, std::size_t size) noexcept
Definition: buffers.hpp:424
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
std
STL namespace.
safe_uint_truncate.hpp
Helpers for safe truncation of unsigned integers.
sendfile_operation_default.ipp