2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 // Official repository: https://github.com/boostorg/beast
10 #ifndef BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP
11 #define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP
13 #if BOOST_BEAST_USE_WIN32_FILE
15 #include <boost/beast/core/async_base.hpp>
16 #include <boost/beast/core/bind_handler.hpp>
17 #include <boost/beast/core/buffers_range.hpp>
18 #include <boost/beast/core/detail/clamp.hpp>
19 #include <boost/beast/core/detail/is_invocable.hpp>
20 #include <boost/beast/http/write.hpp>
21 #include <boost/beast/http/serializer.hpp>
22 #include <boost/asio/async_result.hpp>
23 #include <boost/asio/basic_stream_socket.hpp>
24 #include <boost/asio/windows/overlapped_ptr.hpp>
25 #include <boost/make_unique.hpp>
26 #include <boost/smart_ptr/make_shared_array.hpp>
27 #include <boost/winapi/basic_types.hpp>
28 #include <boost/winapi/get_last_error.hpp>
37 template<class, class, bool, class, class>
38 class write_some_win32_op;
42 struct basic_file_body<file_win32>
44 using file_type = file_win32;
49 //--------------------------------------------------------------------------
55 friend struct basic_file_body<file_win32>;
57 template<class, class, bool, class, class>
58 friend class detail::write_some_win32_op;
60 class Protocol, class Executor,
61 bool isRequest, class Fields>
65 net::basic_stream_socket<Protocol, Executor>& sock,
67 basic_file_body<file_win32>, Fields>& sr,
71 std::uint64_t size_ = 0; // cached file size
72 std::uint64_t first_; // starting offset of the range
73 std::uint64_t last_; // ending offset of the range
76 ~value_type() = default;
77 value_type() = default;
78 value_type(value_type&& other) = default;
79 value_type& operator=(value_type&& other) = default;
84 return file_.is_open();
97 open(char const* path, file_mode mode, error_code& ec);
100 reset(file_win32&& file, error_code& ec);
103 //--------------------------------------------------------------------------
107 template<class, class, bool, class, class>
108 friend class detail::write_some_win32_op;
110 class Protocol, class Executor,
111 bool isRequest, class Fields>
115 net::basic_stream_socket<Protocol, Executor>& sock,
116 serializer<isRequest,
117 basic_file_body<file_win32>, Fields>& sr,
120 value_type& body_; // The body we are reading from
121 std::uint64_t pos_; // The current position in the file
122 char buf_[4096]; // Small buffer for reading
125 using const_buffers_type =
128 template<bool isRequest, class Fields>
129 writer(header<isRequest, Fields>&, value_type& b)
137 BOOST_ASSERT(body_.file_.is_open());
141 boost::optional<std::pair<const_buffers_type, bool>>
144 std::size_t const n = (std::min)(sizeof(buf_),
145 beast::detail::clamp(body_.last_ - pos_));
151 auto const nread = body_.file_.read(buf_, n, ec);
154 BOOST_ASSERT(nread != 0);
158 {buf_, nread}, // buffer to return.
159 pos_ < body_.last_}}; // `true` if there are more buffers.
163 //--------------------------------------------------------------------------
170 template<bool isRequest, class Fields>
172 reader(header<isRequest, Fields>&, value_type& b)
178 init(boost::optional<
179 std::uint64_t> const& content_length,
182 // VFALCO We could reserve space in the file
183 boost::ignore_unused(content_length);
184 BOOST_ASSERT(body_.file_.is_open());
188 template<class ConstBufferSequence>
190 put(ConstBufferSequence const& buffers,
193 std::size_t nwritten = 0;
194 for(auto buffer : beast::buffers_range_ref(buffers))
196 nwritten += body_.file_.write(
197 buffer.data(), buffer.size(), ec);
206 finish(error_code& ec)
212 //--------------------------------------------------------------------------
216 size(value_type const& body)
222 //------------------------------------------------------------------------------
226 basic_file_body<file_win32>::
231 file_.close(ignored);
236 basic_file_body<file_win32>::
238 open(char const* path, file_mode mode, error_code& ec)
240 file_.open(path, mode, ec);
243 size_ = file_.size(ec);
255 basic_file_body<file_win32>::
257 reset(file_win32&& file, error_code& ec)
262 file_.close(ignored);
264 file_ = std::move(file);
267 size_ = file_.size(ec);
278 //------------------------------------------------------------------------------
282 template<class Unsigned>
283 boost::winapi::DWORD_
287 boost::winapi::DWORD_>(
291 template<class Unsigned>
292 boost::winapi::DWORD_
293 highPart(Unsigned n, std::true_type)
296 boost::winapi::DWORD_>(
300 template<class Unsigned>
301 boost::winapi::DWORD_
302 highPart(Unsigned, std::false_type)
307 template<class Unsigned>
308 boost::winapi::DWORD_
311 return highPart(n, std::integral_constant<
312 bool, (sizeof(Unsigned)>4)>{});
318 template<class ConstBufferSequence>
320 operator()(error_code&,
321 ConstBufferSequence const&) const
327 //------------------------------------------------------------------------------
329 #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
332 class Protocol, class Executor,
333 bool isRequest, class Fields,
335 class write_some_win32_op
336 : public beast::async_base<Handler, Executor>
338 net::basic_stream_socket<
339 Protocol, Executor>& sock_;
340 serializer<isRequest,
341 basic_file_body<file_win32>, Fields>& sr_;
342 std::size_t bytes_transferred_ = 0;
343 bool header_ = false;
346 template<class Handler_>
349 net::basic_stream_socket<
350 Protocol, Executor>& s,
351 serializer<isRequest,
352 basic_file_body<file_win32>,Fields>& sr)
355 std::forward<Handler_>(h),
366 if(! sr_.is_header_done())
370 return detail::async_write_some_impl(
371 sock_, sr_, std::move(*this));
373 if(sr_.get().chunked())
375 return detail::async_write_some_impl(
376 sock_, sr_, std::move(*this));
378 auto& w = sr_.writer_impl();
379 boost::winapi::DWORD_ const nNumberOfBytesToWrite =
380 static_cast<boost::winapi::DWORD_>(
381 (std::min<std::uint64_t>)(
382 (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr_.limit()),
383 (std::numeric_limits<boost::winapi::DWORD_>::max)()));
384 net::windows::overlapped_ptr overlapped{
385 sock_.get_executor(), std::move(*this)};
386 // Note that we have moved *this, so we cannot access
387 // the handler since it is now moved-from. We can still
388 // access simple things like references and built-in types.
389 auto& ov = *overlapped.get();
390 ov.Offset = lowPart(w.pos_);
391 ov.OffsetHigh = highPart(w.pos_);
392 auto const bSuccess = ::TransmitFile(
393 sock_.native_handle(),
394 sr_.get().body().file_.native_handle(),
395 nNumberOfBytesToWrite,
400 auto const dwError = boost::winapi::GetLastError();
401 if(! bSuccess && dwError !=
402 boost::winapi::ERROR_IO_PENDING_)
404 // VFALCO This needs review, is 0 the right number?
405 // completed immediately (with error?)
406 overlapped.complete(error_code{static_cast<int>(dwError),
407 system_category()}, 0);
410 overlapped.release();
416 std::size_t bytes_transferred = 0)
418 bytes_transferred_ += bytes_transferred;
426 auto& w = sr_.writer_impl();
427 w.pos_ += bytes_transferred;
428 BOOST_ASSERT(w.pos_ <= w.body_.last_);
429 if(w.pos_ >= w.body_.last_)
431 sr_.next(ec, null_lambda{});
433 BOOST_ASSERT(sr_.is_done());
436 this->complete_now(ec, bytes_transferred_);
440 struct run_write_some_win32_op
443 class Protocol, class Executor,
444 bool isRequest, class Fields,
449 net::basic_stream_socket<
450 Protocol, Executor>* s,
451 serializer<isRequest,
452 basic_file_body<file_win32>, Fields>* sr)
454 // If you get an error on the following line it means
455 // that your handler does not meet the documented type
456 // requirements for the handler.
459 beast::detail::is_invocable<WriteHandler,
460 void(error_code, std::size_t)>::value,
461 "WriteHandler type requirements not met");
466 typename std::decay<WriteHandler>::type>(
467 std::forward<WriteHandler>(h), *s, *sr);
475 //------------------------------------------------------------------------------
478 class Protocol, class Executor,
479 bool isRequest, class Fields>
482 net::basic_stream_socket<
483 Protocol, Executor>& sock,
484 serializer<isRequest,
485 basic_file_body<file_win32>, Fields>& sr,
488 if(! sr.is_header_done())
491 auto const bytes_transferred =
492 detail::write_some_impl(sock, sr, ec);
494 return bytes_transferred;
495 return bytes_transferred;
497 if(sr.get().chunked())
499 auto const bytes_transferred =
500 detail::write_some_impl(sock, sr, ec);
502 return bytes_transferred;
503 return bytes_transferred;
505 auto& w = sr.writer_impl();
506 w.body_.file_.seek(w.pos_, ec);
509 boost::winapi::DWORD_ const nNumberOfBytesToWrite =
510 static_cast<boost::winapi::DWORD_>(
511 (std::min<std::uint64_t>)(
512 (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr.limit()),
513 (std::numeric_limits<boost::winapi::DWORD_>::max)()));
514 auto const bSuccess = ::TransmitFile(
515 sock.native_handle(),
516 w.body_.file_.native_handle(),
517 nNumberOfBytesToWrite,
524 ec.assign(static_cast<int>(
525 boost::winapi::GetLastError()),
529 w.pos_ += nNumberOfBytesToWrite;
530 BOOST_ASSERT(w.pos_ <= w.body_.last_);
531 if(w.pos_ < w.body_.last_)
537 sr.next(ec, detail::null_lambda{});
539 BOOST_ASSERT(sr.is_done());
541 return nNumberOfBytesToWrite;
544 #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
547 class Protocol, class Executor,
548 bool isRequest, class Fields,
550 BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
552 net::basic_stream_socket<
553 Protocol, Executor>& sock,
554 serializer<isRequest,
555 basic_file_body<file_win32>, Fields>& sr,
556 WriteHandler&& handler)
558 return net::async_initiate<
560 void(error_code, std::size_t)>(
561 detail::run_write_some_win32_op{},