// // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/beast // #ifndef BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP #define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP #if BOOST_BEAST_USE_WIN32_FILE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace beast { namespace http { namespace detail { template class write_some_win32_op; } // detail template<> struct basic_file_body { using file_type = file_win32; class writer; class reader; //-------------------------------------------------------------------------- class value_type { friend class writer; friend class reader; friend struct basic_file_body; template friend class detail::write_some_win32_op; template< class Protocol, class Executor, bool isRequest, class Fields> friend std::size_t write_some( net::basic_stream_socket& sock, serializer, Fields>& sr, error_code& ec); file_win32 file_; std::uint64_t size_ = 0; // cached file size std::uint64_t first_; // starting offset of the range std::uint64_t last_; // ending offset of the range public: ~value_type() = default; value_type() = default; value_type(value_type&& other) = default; value_type& operator=(value_type&& other) = default; bool is_open() const { return file_.is_open(); } std::uint64_t size() const { return size_; } void close(); void open(char const* path, file_mode mode, error_code& ec); void reset(file_win32&& file, error_code& ec); }; //-------------------------------------------------------------------------- class writer { template friend class detail::write_some_win32_op; template< class Protocol, class Executor, bool isRequest, class Fields> friend std::size_t write_some( net::basic_stream_socket& sock, serializer, Fields>& sr, error_code& ec); value_type& body_; // The body we are reading from std::uint64_t pos_; // The current position in the file char buf_[4096]; // Small buffer for reading public: using const_buffers_type = net::const_buffer; template writer(header&, value_type& b) : body_(b) { } void init(error_code&) { BOOST_ASSERT(body_.file_.is_open()); pos_ = body_.first_; } boost::optional> get(error_code& ec) { std::size_t const n = (std::min)(sizeof(buf_), beast::detail::clamp(body_.last_ - pos_)); if(n == 0) { ec = {}; return boost::none; } auto const nread = body_.file_.read(buf_, n, ec); if(ec) return boost::none; BOOST_ASSERT(nread != 0); pos_ += nread; ec = {}; return {{ {buf_, nread}, // buffer to return. pos_ < body_.last_}}; // `true` if there are more buffers. } }; //-------------------------------------------------------------------------- class reader { value_type& body_; public: template explicit reader(header&, value_type& b) : body_(b) { } void init(boost::optional< std::uint64_t> const& content_length, error_code& ec) { // VFALCO We could reserve space in the file boost::ignore_unused(content_length); BOOST_ASSERT(body_.file_.is_open()); ec = {}; } template std::size_t put(ConstBufferSequence const& buffers, error_code& ec) { std::size_t nwritten = 0; for(auto buffer : beast::buffers_range_ref(buffers)) { nwritten += body_.file_.write( buffer.data(), buffer.size(), ec); if(ec) return nwritten; } ec = {}; return nwritten; } void finish(error_code& ec) { ec = {}; } }; //-------------------------------------------------------------------------- static std::uint64_t size(value_type const& body) { return body.size(); } }; //------------------------------------------------------------------------------ inline void basic_file_body:: value_type:: close() { error_code ignored; file_.close(ignored); } inline void basic_file_body:: value_type:: open(char const* path, file_mode mode, error_code& ec) { file_.open(path, mode, ec); if(ec) return; size_ = file_.size(ec); if(ec) { close(); return; } first_ = 0; last_ = size_; } inline void basic_file_body:: value_type:: reset(file_win32&& file, error_code& ec) { if(file_.is_open()) { error_code ignored; file_.close(ignored); } file_ = std::move(file); if(file_.is_open()) { size_ = file_.size(ec); if(ec) { close(); return; } first_ = 0; last_ = size_; } } //------------------------------------------------------------------------------ namespace detail { template boost::winapi::DWORD_ lowPart(Unsigned n) { return static_cast< boost::winapi::DWORD_>( n & 0xffffffff); } template boost::winapi::DWORD_ highPart(Unsigned n, std::true_type) { return static_cast< boost::winapi::DWORD_>( (n>>32)&0xffffffff); } template boost::winapi::DWORD_ highPart(Unsigned, std::false_type) { return 0; } template boost::winapi::DWORD_ highPart(Unsigned n) { return highPart(n, std::integral_constant< bool, (sizeof(Unsigned)>4)>{}); } class null_lambda { public: template void operator()(error_code&, ConstBufferSequence const&) const { BOOST_ASSERT(false); } }; //------------------------------------------------------------------------------ #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR template< class Protocol, class Executor, bool isRequest, class Fields, class Handler> class write_some_win32_op : public beast::async_base { net::basic_stream_socket< Protocol, Executor>& sock_; serializer, Fields>& sr_; std::size_t bytes_transferred_ = 0; bool header_ = false; public: template write_some_win32_op( Handler_&& h, net::basic_stream_socket< Protocol, Executor>& s, serializer,Fields>& sr) : async_base< Handler, Executor>( std::forward(h), s.get_executor()) , sock_(s) , sr_(sr) { (*this)(); } void operator()() { if(! sr_.is_header_done()) { header_ = true; sr_.split(true); return detail::async_write_some_impl( sock_, sr_, std::move(*this)); } if(sr_.get().chunked()) { return detail::async_write_some_impl( sock_, sr_, std::move(*this)); } auto& w = sr_.writer_impl(); boost::winapi::DWORD_ const nNumberOfBytesToWrite = static_cast( (std::min)( (std::min)(w.body_.last_ - w.pos_, sr_.limit()), (std::numeric_limits::max)())); net::windows::overlapped_ptr overlapped{ sock_.get_executor(), std::move(*this)}; // Note that we have moved *this, so we cannot access // the handler since it is now moved-from. We can still // access simple things like references and built-in types. auto& ov = *overlapped.get(); ov.Offset = lowPart(w.pos_); ov.OffsetHigh = highPart(w.pos_); auto const bSuccess = ::TransmitFile( sock_.native_handle(), sr_.get().body().file_.native_handle(), nNumberOfBytesToWrite, 0, overlapped.get(), nullptr, 0); auto const dwError = boost::winapi::GetLastError(); if(! bSuccess && dwError != boost::winapi::ERROR_IO_PENDING_) { // VFALCO This needs review, is 0 the right number? // completed immediately (with error?) overlapped.complete(error_code{static_cast(dwError), system_category()}, 0); return; } overlapped.release(); } void operator()( error_code ec, std::size_t bytes_transferred = 0) { bytes_transferred_ += bytes_transferred; if(! ec) { if(header_) { header_ = false; return (*this)(); } auto& w = sr_.writer_impl(); w.pos_ += bytes_transferred; BOOST_ASSERT(w.pos_ <= w.body_.last_); if(w.pos_ >= w.body_.last_) { sr_.next(ec, null_lambda{}); BOOST_ASSERT(! ec); BOOST_ASSERT(sr_.is_done()); } } this->complete_now(ec, bytes_transferred_); } }; struct run_write_some_win32_op { template< class Protocol, class Executor, bool isRequest, class Fields, class WriteHandler> void operator()( WriteHandler&& h, net::basic_stream_socket< Protocol, Executor>* s, serializer, Fields>* sr) { // If you get an error on the following line it means // that your handler does not meet the documented type // requirements for the handler. static_assert( beast::detail::is_invocable::value, "WriteHandler type requirements not met"); write_some_win32_op< Protocol, Executor, isRequest, Fields, typename std::decay::type>( std::forward(h), *s, *sr); } }; #endif } // detail //------------------------------------------------------------------------------ template< class Protocol, class Executor, bool isRequest, class Fields> std::size_t write_some( net::basic_stream_socket< Protocol, Executor>& sock, serializer, Fields>& sr, error_code& ec) { if(! sr.is_header_done()) { sr.split(true); auto const bytes_transferred = detail::write_some_impl(sock, sr, ec); if(ec) return bytes_transferred; return bytes_transferred; } if(sr.get().chunked()) { auto const bytes_transferred = detail::write_some_impl(sock, sr, ec); if(ec) return bytes_transferred; return bytes_transferred; } auto& w = sr.writer_impl(); w.body_.file_.seek(w.pos_, ec); if(ec) return 0; boost::winapi::DWORD_ const nNumberOfBytesToWrite = static_cast( (std::min)( (std::min)(w.body_.last_ - w.pos_, sr.limit()), (std::numeric_limits::max)())); auto const bSuccess = ::TransmitFile( sock.native_handle(), w.body_.file_.native_handle(), nNumberOfBytesToWrite, 0, nullptr, nullptr, 0); if(! bSuccess) { ec.assign(static_cast( boost::winapi::GetLastError()), system_category()); return 0; } w.pos_ += nNumberOfBytesToWrite; BOOST_ASSERT(w.pos_ <= w.body_.last_); if(w.pos_ < w.body_.last_) { ec = {}; } else { sr.next(ec, detail::null_lambda{}); BOOST_ASSERT(! ec); BOOST_ASSERT(sr.is_done()); } return nNumberOfBytesToWrite; } #if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR template< class Protocol, class Executor, bool isRequest, class Fields, class WriteHandler> BOOST_BEAST_ASYNC_RESULT2(WriteHandler) async_write_some( net::basic_stream_socket< Protocol, Executor>& sock, serializer, Fields>& sr, WriteHandler&& handler) { return net::async_initiate< WriteHandler, void(error_code, std::size_t)>( detail::run_write_some_win32_op{}, handler, &sock, &sr); } #endif } // http } // beast } // boost #endif #endif