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
15namespace restinio
16{
17
18namespace impl
19{
20
21namespace asio_details
22{
23
24#if RESTINIO_ASIO_VERSION < 101300
25
26template<typename Socket >
27decltype(auto)
28executor_or_context_from_socket( Socket & socket )
29{
30 return socket.get_executor().context();
31}
32
33#else
34
35template<typename Socket >
36decltype(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
51template < typename Socket >
52class 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(
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 {
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 {
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),
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 {
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
208template <>
209class 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 {
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 {
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(
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.
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)
auto make_async_write_handler() noexcept
Helper method for making a lambda for async_write completion handler.
sendfile_operation_runner_t(const sendfile_operation_runner_t &)=delete
sendfile_operation_runner_base_t< Socket > base_type_t
sendfile_operation_runner_t & operator=(const sendfile_operation_runner_t &)=delete
constexpr auto eof
std::function< void(const asio_ns::error_code &, file_size_t) > after_sendfile_cb_t
Callback type for invocation when sendfile operation completes.
auto make_error_code(const Error_Type &e) noexcept
constexpr http_status_code_t ok
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...
asio_ns::executor default_asio_executor
@ async_read_some_at_call_failed
A call to async_read_some_at failed. The corresponding sendfile operation wasn't done.
@ async_write_call_failed
A call to async_write failed. The corresponding write operation wasn't done.
constexpr const_buffer_t const_buffer(const void *str, std::size_t size) noexcept
Definition: buffers.hpp:424
asio_ns::error_code make_asio_compaible_error(asio_convertible_error_t err) noexcept
Make restinio error_code compatible with asio_ns::error_code.
STL namespace.
Helpers for safe truncation of unsigned integers.