2 // Copyright (c) 2016-2017 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_IPP
11 #define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
13 #if BOOST_BEAST_USE_WIN32_FILE
15 #include <boost/beast/core/bind_handler.hpp>
16 #include <boost/beast/core/type_traits.hpp>
17 #include <boost/beast/core/detail/clamp.hpp>
18 #include <boost/beast/http/serializer.hpp>
19 #include <boost/asio/associated_allocator.hpp>
20 #include <boost/asio/associated_executor.hpp>
21 #include <boost/asio/async_result.hpp>
22 #include <boost/asio/basic_stream_socket.hpp>
23 #include <boost/asio/handler_continuation_hook.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>
36 template<class, class, bool, class>
37 class write_some_win32_op;
41 struct basic_file_body<file_win32>
43 using file_type = file_win32;
48 //--------------------------------------------------------------------------
54 friend struct basic_file_body<file_win32>;
56 template<class, class, bool, class>
57 friend class detail::write_some_win32_op;
59 class Protocol, bool isRequest, class Fields>
63 boost::asio::basic_stream_socket<Protocol>& sock,
65 basic_file_body<file_win32>, Fields>& sr,
69 std::uint64_t size_ = 0; // cached file size
70 std::uint64_t first_; // starting offset of the range
71 std::uint64_t last_; // ending offset of the range
74 ~value_type() = default;
75 value_type() = default;
76 value_type(value_type&& other) = default;
77 value_type& operator=(value_type&& other) = default;
82 return file_.is_open();
95 open(char const* path, file_mode mode, error_code& ec);
98 reset(file_win32&& file, error_code& ec);
101 //--------------------------------------------------------------------------
105 template<class, class, bool, class>
106 friend class detail::write_some_win32_op;
108 class Protocol, bool isRequest, class Fields>
112 boost::asio::basic_stream_socket<Protocol>& sock,
113 serializer<isRequest,
114 basic_file_body<file_win32>, Fields>& sr,
117 value_type& body_; // The body we are reading from
118 std::uint64_t pos_; // The current position in the file
119 char buf_[4096]; // Small buffer for reading
122 using const_buffers_type =
123 boost::asio::const_buffer;
125 template<bool isRequest, class Fields>
126 writer(message<isRequest,
127 basic_file_body<file_win32>, Fields>& m)
135 BOOST_ASSERT(body_.file_.is_open());
139 boost::optional<std::pair<const_buffers_type, bool>>
142 std::size_t const n = (std::min)(sizeof(buf_),
143 beast::detail::clamp(body_.last_ - pos_));
146 ec.assign(0, ec.category());
149 auto const nread = body_.file_.read(buf_, n, ec);
152 BOOST_ASSERT(nread != 0);
154 ec.assign(0, ec.category());
156 {buf_, nread}, // buffer to return.
157 pos_ < body_.last_}}; // `true` if there are more buffers.
161 //--------------------------------------------------------------------------
168 template<bool isRequest, class Fields>
170 reader(message<isRequest, basic_file_body, Fields>& m)
176 init(boost::optional<
177 std::uint64_t> const& content_length,
180 // VFALCO We could reserve space in the file
181 boost::ignore_unused(content_length);
182 BOOST_ASSERT(body_.file_.is_open());
183 ec.assign(0, ec.category());
186 template<class ConstBufferSequence>
188 put(ConstBufferSequence const& buffers,
191 std::size_t nwritten = 0;
192 for(auto buffer : beast::detail::buffers_range(buffers))
194 nwritten += body_.file_.write(
195 buffer.data(), buffer.size(), ec);
199 ec.assign(0, ec.category());
204 finish(error_code& ec)
206 ec.assign(0, ec.category());
210 //--------------------------------------------------------------------------
214 size(value_type const& body)
220 //------------------------------------------------------------------------------
224 basic_file_body<file_win32>::
229 file_.close(ignored);
234 basic_file_body<file_win32>::
236 open(char const* path, file_mode mode, error_code& ec)
238 file_.open(path, mode, ec);
241 size_ = file_.size(ec);
253 basic_file_body<file_win32>::
255 reset(file_win32&& file, error_code& ec)
260 file_.close(ignored);
262 file_ = std::move(file);
265 size_ = file_.size(ec);
276 //------------------------------------------------------------------------------
280 template<class Unsigned>
282 boost::winapi::DWORD_
286 boost::winapi::DWORD_>(
290 template<class Unsigned>
292 boost::winapi::DWORD_
293 highPart(Unsigned n, std::true_type)
296 boost::winapi::DWORD_>(
300 template<class Unsigned>
302 boost::winapi::DWORD_
303 highPart(Unsigned, std::false_type)
308 template<class Unsigned>
310 boost::winapi::DWORD_
313 return highPart(n, std::integral_constant<
314 bool, (sizeof(Unsigned)>4)>{});
320 template<class ConstBufferSequence>
322 operator()(error_code&,
323 ConstBufferSequence const&) const
329 //------------------------------------------------------------------------------
331 #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
334 class Protocol, class Handler,
335 bool isRequest, class Fields>
336 class write_some_win32_op
338 boost::asio::basic_stream_socket<Protocol>& sock_;
339 serializer<isRequest,
340 basic_file_body<file_win32>, Fields>& sr_;
341 std::size_t bytes_transferred_ = 0;
343 bool header_ = false;
346 write_some_win32_op(write_some_win32_op&&) = default;
347 write_some_win32_op(write_some_win32_op const&) = default;
349 template<class DeducedHandler>
352 boost::asio::basic_stream_socket<Protocol>& s,
353 serializer<isRequest,
354 basic_file_body<file_win32>,Fields>& sr)
357 , h_(std::forward<DeducedHandler>(h))
361 using allocator_type =
362 boost::asio::associated_allocator_t<Handler>;
365 get_allocator() const noexcept
367 return boost::asio::get_associated_allocator(h_);
370 using executor_type =
371 boost::asio::associated_executor_t<Handler, decltype(std::declval<
372 boost::asio::basic_stream_socket<Protocol>&>().get_executor())>;
375 get_executor() const noexcept
377 return boost::asio::get_associated_executor(
378 h_, sock_.get_executor());
387 std::size_t bytes_transferred = 0);
390 bool asio_handler_is_continuation(write_some_win32_op* op)
392 using boost::asio::asio_handler_is_continuation;
393 return asio_handler_is_continuation(
394 std::addressof(op->h_));
399 class Protocol, class Handler,
400 bool isRequest, class Fields>
403 Protocol, Handler, isRequest, Fields>::
406 if(! sr_.is_header_done())
410 return detail::async_write_some(
411 sock_, sr_, std::move(*this));
413 if(sr_.get().chunked())
415 return detail::async_write_some(
416 sock_, sr_, std::move(*this));
418 auto& r = sr_.reader_impl();
419 boost::winapi::DWORD_ const nNumberOfBytesToWrite =
420 static_cast<boost::winapi::DWORD_>(
421 (std::min<std::uint64_t>)(
422 (std::min<std::uint64_t>)(r.body_.last_ - r.pos_, sr_.limit()),
423 (std::numeric_limits<boost::winapi::DWORD_>::max)()));
424 boost::asio::windows::overlapped_ptr overlapped{
425 sock_.get_executor().context(), *this};
426 auto& ov = *overlapped.get();
427 ov.Offset = lowPart(r.pos_);
428 ov.OffsetHigh = highPart(r.pos_);
429 auto const bSuccess = ::TransmitFile(
430 sock_.native_handle(),
431 sr_.get().body().file_.native_handle(),
432 nNumberOfBytesToWrite,
437 auto const dwError = ::GetLastError();
438 if(! bSuccess && dwError !=
439 boost::winapi::ERROR_IO_PENDING_)
441 // VFALCO This needs review, is 0 the right number?
442 // completed immediately (with error?)
443 overlapped.complete(error_code{static_cast<int>(
444 boost::winapi::GetLastError()),
445 system_category()}, 0);
448 overlapped.release();
452 class Protocol, class Handler,
453 bool isRequest, class Fields>
456 Protocol, Handler, isRequest, Fields>::
458 error_code ec, std::size_t bytes_transferred)
460 bytes_transferred_ += bytes_transferred;
468 auto& r = sr_.reader_impl();
469 r.pos_ += bytes_transferred;
470 BOOST_ASSERT(r.pos_ <= r.body_.last_);
471 if(r.pos_ >= r.body_.last_)
473 sr_.next(ec, null_lambda{});
475 BOOST_ASSERT(sr_.is_done());
478 h_(ec, bytes_transferred_);
485 //------------------------------------------------------------------------------
487 template<class Protocol, bool isRequest, class Fields>
490 boost::asio::basic_stream_socket<Protocol>& sock,
491 serializer<isRequest,
492 basic_file_body<file_win32>, Fields>& sr,
495 if(! sr.is_header_done())
498 auto const bytes_transferred =
499 detail::write_some(sock, sr, ec);
501 return bytes_transferred;
502 return bytes_transferred;
504 if(sr.get().chunked())
506 auto const bytes_transferred =
507 detail::write_some(sock, sr, ec);
509 return bytes_transferred;
510 return bytes_transferred;
512 auto& r = sr.reader_impl();
513 r.body_.file_.seek(r.pos_, ec);
516 boost::winapi::DWORD_ const nNumberOfBytesToWrite =
517 static_cast<boost::winapi::DWORD_>(
518 (std::min<std::uint64_t>)(
519 (std::min<std::uint64_t>)(r.body_.last_ - r.pos_, sr.limit()),
520 (std::numeric_limits<boost::winapi::DWORD_>::max)()));
521 auto const bSuccess = ::TransmitFile(
522 sock.native_handle(),
523 r.body_.file_.native_handle(),
524 nNumberOfBytesToWrite,
531 ec.assign(static_cast<int>(
532 boost::winapi::GetLastError()),
536 r.pos_ += nNumberOfBytesToWrite;
537 BOOST_ASSERT(r.pos_ <= r.body_.last_);
538 if(r.pos_ < r.body_.last_)
540 ec.assign(0, ec.category());
544 sr.next(ec, detail::null_lambda{});
546 BOOST_ASSERT(sr.is_done());
548 return nNumberOfBytesToWrite;
551 #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
555 bool isRequest, class Fields,
557 BOOST_ASIO_INITFN_RESULT_TYPE(
558 WriteHandler, void(error_code, std::size_t))
560 boost::asio::basic_stream_socket<Protocol>& sock,
561 serializer<isRequest,
562 basic_file_body<file_win32>, Fields>& sr,
563 WriteHandler&& handler)
565 boost::asio::async_completion<WriteHandler,
566 void(error_code)> init{handler};
567 detail::write_some_win32_op<
569 BOOST_ASIO_HANDLER_TYPE(WriteHandler,
570 void(error_code, std::size_t)),
572 init.completion_handler, sock, sr}();
573 return init.result.get();