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/error.hpp>
21 #include <boost/beast/http/write.hpp>
22 #include <boost/beast/http/serializer.hpp>
23 #include <boost/asio/async_result.hpp>
24 #include <boost/asio/basic_stream_socket.hpp>
25 #include <boost/asio/windows/overlapped_ptr.hpp>
26 #include <boost/make_unique.hpp>
27 #include <boost/smart_ptr/make_shared_array.hpp>
28 #include <boost/winapi/basic_types.hpp>
29 #include <boost/winapi/error_codes.hpp>
30 #include <boost/winapi/get_last_error.hpp>
39 template<class, class, bool, class, class>
40 class write_some_win32_op;
44 struct basic_file_body<file_win32>
46 using file_type = file_win32;
51 //--------------------------------------------------------------------------
57 friend struct basic_file_body<file_win32>;
59 template<class, class, bool, class, class>
60 friend class detail::write_some_win32_op;
62 class Protocol, class Executor,
63 bool isRequest, class Fields>
67 net::basic_stream_socket<Protocol, Executor>& sock,
69 basic_file_body<file_win32>, Fields>& sr,
73 std::uint64_t size_ = 0; // cached file size
74 std::uint64_t first_; // starting offset of the range
75 std::uint64_t last_; // ending offset of the range
78 ~value_type() = default;
79 value_type() = default;
80 value_type(value_type&& other) = default;
81 value_type& operator=(value_type&& other) = default;
91 return file_.is_open();
104 open(char const* path, file_mode mode, error_code& ec);
107 reset(file_win32&& file, error_code& ec);
110 //--------------------------------------------------------------------------
114 template<class, class, bool, class, class>
115 friend class detail::write_some_win32_op;
117 class Protocol, class Executor,
118 bool isRequest, class Fields>
122 net::basic_stream_socket<Protocol, Executor>& sock,
123 serializer<isRequest,
124 basic_file_body<file_win32>, Fields>& sr,
127 value_type& body_; // The body we are reading from
128 std::uint64_t pos_; // The current position in the file
129 char buf_[4096]; // Small buffer for reading
132 using const_buffers_type =
135 template<bool isRequest, class Fields>
136 writer(header<isRequest, Fields>&, value_type& b)
140 BOOST_ASSERT(body_.file_.is_open());
146 BOOST_ASSERT(body_.file_.is_open());
150 boost::optional<std::pair<const_buffers_type, bool>>
153 std::size_t const n = (std::min)(sizeof(buf_),
154 beast::detail::clamp(body_.last_ - pos_));
160 auto const nread = body_.file_.read(buf_, n, ec);
165 ec = error::short_read;
168 BOOST_ASSERT(nread != 0);
172 {buf_, nread}, // buffer to return.
173 pos_ < body_.last_}}; // `true` if there are more buffers.
177 //--------------------------------------------------------------------------
184 template<bool isRequest, class Fields>
186 reader(header<isRequest, Fields>&, value_type& b)
192 init(boost::optional<
193 std::uint64_t> const& content_length,
196 // VFALCO We could reserve space in the file
197 boost::ignore_unused(content_length);
198 BOOST_ASSERT(body_.file_.is_open());
202 template<class ConstBufferSequence>
204 put(ConstBufferSequence const& buffers,
207 std::size_t nwritten = 0;
208 for(auto buffer : beast::buffers_range_ref(buffers))
210 nwritten += body_.file_.write(
211 buffer.data(), buffer.size(), ec);
220 finish(error_code& ec)
226 //--------------------------------------------------------------------------
230 size(value_type const& body)
236 //------------------------------------------------------------------------------
240 basic_file_body<file_win32>::
245 file_.close(ignored);
250 basic_file_body<file_win32>::
252 open(char const* path, file_mode mode, error_code& ec)
254 file_.open(path, mode, ec);
257 size_ = file_.size(ec);
269 basic_file_body<file_win32>::
271 reset(file_win32&& file, error_code& ec)
276 file_.close(ignored);
278 file_ = std::move(file);
281 size_ = file_.size(ec);
292 //------------------------------------------------------------------------------
296 template<class Unsigned>
297 boost::winapi::DWORD_
301 boost::winapi::DWORD_>(
305 template<class Unsigned>
306 boost::winapi::DWORD_
307 highPart(Unsigned n, std::true_type)
310 boost::winapi::DWORD_>(
314 template<class Unsigned>
315 boost::winapi::DWORD_
316 highPart(Unsigned, std::false_type)
321 template<class Unsigned>
322 boost::winapi::DWORD_
325 return highPart(n, std::integral_constant<
326 bool, (sizeof(Unsigned)>4)>{});
332 template<class ConstBufferSequence>
334 operator()(error_code&,
335 ConstBufferSequence const&) const
341 // https://github.com/boostorg/beast/issues/1815
342 // developer commentary:
343 // This function mimics the behaviour of ASIO.
344 // Perhaps the correct fix is to insist on the use
345 // of an appropriate error_condition to detect
346 // connection_reset and connection_refused?
350 boost::winapi::DWORD_ dwError) noexcept
353 // https://github.com/boostorg/asio/blob/6534af41b471288091ae39f9ab801594189b6fc9/include/boost/asio/detail/impl/socket_ops.ipp#L842
356 case boost::winapi::ERROR_NETNAME_DELETED_:
357 return net::error::connection_reset;
358 case boost::winapi::ERROR_PORT_UNREACHABLE_:
359 return net::error::connection_refused;
360 case boost::winapi::WSAEMSGSIZE_:
361 case boost::winapi::ERROR_MORE_DATA_:
365 static_cast<int>(dwError),
372 error_code ec) noexcept
377 return make_win32_error(
378 static_cast<boost::winapi::DWORD_>(
382 //------------------------------------------------------------------------------
384 #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
387 class Protocol, class Executor,
388 bool isRequest, class Fields,
390 class write_some_win32_op
391 : public beast::async_base<Handler, Executor>
393 net::basic_stream_socket<
394 Protocol, Executor>& sock_;
395 serializer<isRequest,
396 basic_file_body<file_win32>, Fields>& sr_;
397 bool header_ = false;
400 template<class Handler_>
403 net::basic_stream_socket<
404 Protocol, Executor>& s,
405 serializer<isRequest,
406 basic_file_body<file_win32>,Fields>& sr)
409 std::forward<Handler_>(h),
420 if(! sr_.is_header_done())
424 return detail::async_write_some_impl(
425 sock_, sr_, std::move(*this));
427 if(sr_.get().chunked())
429 return detail::async_write_some_impl(
430 sock_, sr_, std::move(*this));
432 auto& w = sr_.writer_impl();
433 boost::winapi::DWORD_ const nNumberOfBytesToWrite =
434 static_cast<boost::winapi::DWORD_>(
435 (std::min<std::uint64_t>)(
436 (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr_.limit()),
437 (std::numeric_limits<boost::winapi::DWORD_>::max)()));
438 net::windows::overlapped_ptr overlapped{
439 sock_.get_executor(), std::move(*this)};
440 // Note that we have moved *this, so we cannot access
441 // the handler since it is now moved-from. We can still
442 // access simple things like references and built-in types.
443 auto& ov = *overlapped.get();
444 ov.Offset = lowPart(w.pos_);
445 ov.OffsetHigh = highPart(w.pos_);
446 auto const bSuccess = ::TransmitFile(
447 sock_.native_handle(),
448 sr_.get().body().file_.native_handle(),
449 nNumberOfBytesToWrite,
454 auto const dwError = boost::winapi::GetLastError();
455 if(! bSuccess && dwError !=
456 boost::winapi::ERROR_IO_PENDING_)
458 // VFALCO This needs review, is 0 the right number?
459 // completed immediately (with error?)
461 make_win32_error(dwError), 0);
464 overlapped.release();
470 std::size_t bytes_transferred = 0)
474 ec = make_win32_error(ec);
476 else if(! ec && ! header_)
478 auto& w = sr_.writer_impl();
479 w.pos_ += bytes_transferred;
480 BOOST_ASSERT(w.pos_ <= w.body_.last_);
481 if(w.pos_ >= w.body_.last_)
483 sr_.next(ec, null_lambda{});
485 BOOST_ASSERT(sr_.is_done());
488 this->complete_now(ec, bytes_transferred);
492 struct run_write_some_win32_op
495 class Protocol, class Executor,
496 bool isRequest, class Fields,
501 net::basic_stream_socket<
502 Protocol, Executor>* s,
503 serializer<isRequest,
504 basic_file_body<file_win32>, Fields>* sr)
506 // If you get an error on the following line it means
507 // that your handler does not meet the documented type
508 // requirements for the handler.
511 beast::detail::is_invocable<WriteHandler,
512 void(error_code, std::size_t)>::value,
513 "WriteHandler type requirements not met");
518 typename std::decay<WriteHandler>::type>(
519 std::forward<WriteHandler>(h), *s, *sr);
527 //------------------------------------------------------------------------------
530 class Protocol, class Executor,
531 bool isRequest, class Fields>
534 net::basic_stream_socket<
535 Protocol, Executor>& sock,
536 serializer<isRequest,
537 basic_file_body<file_win32>, Fields>& sr,
540 if(! sr.is_header_done())
543 auto const bytes_transferred =
544 detail::write_some_impl(sock, sr, ec);
546 return bytes_transferred;
547 return bytes_transferred;
549 if(sr.get().chunked())
551 auto const bytes_transferred =
552 detail::write_some_impl(sock, sr, ec);
554 return bytes_transferred;
555 return bytes_transferred;
557 auto& w = sr.writer_impl();
558 w.body_.file_.seek(w.pos_, ec);
561 boost::winapi::DWORD_ const nNumberOfBytesToWrite =
562 static_cast<boost::winapi::DWORD_>(
563 (std::min<std::uint64_t>)(
564 (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr.limit()),
565 (std::numeric_limits<boost::winapi::DWORD_>::max)()));
566 auto const bSuccess = ::TransmitFile(
567 sock.native_handle(),
568 w.body_.file_.native_handle(),
569 nNumberOfBytesToWrite,
576 ec = detail::make_win32_error(
577 boost::winapi::GetLastError());
580 w.pos_ += nNumberOfBytesToWrite;
581 BOOST_ASSERT(w.pos_ <= w.body_.last_);
582 if(w.pos_ < w.body_.last_)
588 sr.next(ec, detail::null_lambda{});
590 BOOST_ASSERT(sr.is_done());
592 return nNumberOfBytesToWrite;
595 #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
598 class Protocol, class Executor,
599 bool isRequest, class Fields,
600 BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
601 BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
603 net::basic_stream_socket<
604 Protocol, Executor>& sock,
605 serializer<isRequest,
606 basic_file_body<file_win32>, Fields>& sr,
607 WriteHandler&& handler)
609 return net::async_initiate<
611 void(error_code, std::size_t)>(
612 detail::run_write_some_win32_op{},