summaryrefslogtreecommitdiff
path: root/boost/beast/http/impl/file_body_win32.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/beast/http/impl/file_body_win32.hpp')
-rw-r--r--boost/beast/http/impl/file_body_win32.hpp573
1 files changed, 573 insertions, 0 deletions
diff --git a/boost/beast/http/impl/file_body_win32.hpp b/boost/beast/http/impl/file_body_win32.hpp
new file mode 100644
index 0000000000..fc74c49772
--- /dev/null
+++ b/boost/beast/http/impl/file_body_win32.hpp
@@ -0,0 +1,573 @@
+//
+// 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 <boost/beast/core/async_base.hpp>
+#include <boost/beast/core/bind_handler.hpp>
+#include <boost/beast/core/buffers_range.hpp>
+#include <boost/beast/core/detail/clamp.hpp>
+#include <boost/beast/http/serializer.hpp>
+#include <boost/asio/async_result.hpp>
+#include <boost/asio/basic_stream_socket.hpp>
+#include <boost/asio/windows/overlapped_ptr.hpp>
+#include <boost/make_unique.hpp>
+#include <boost/smart_ptr/make_shared_array.hpp>
+#include <boost/winapi/basic_types.hpp>
+#include <boost/winapi/get_last_error.hpp>
+#include <algorithm>
+#include <cstring>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+namespace detail {
+template<class, class, bool, class, class>
+class write_some_win32_op;
+} // detail
+
+template<>
+struct basic_file_body<file_win32>
+{
+ using file_type = file_win32;
+
+ class writer;
+ class reader;
+
+ //--------------------------------------------------------------------------
+
+ class value_type
+ {
+ friend class writer;
+ friend class reader;
+ friend struct basic_file_body<file_win32>;
+
+ template<class, class, bool, class, class>
+ 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<Protocol, Executor>& sock,
+ serializer<isRequest,
+ basic_file_body<file_win32>, 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<class, class, bool, class, class>
+ 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<Protocol, Executor>& sock,
+ serializer<isRequest,
+ basic_file_body<file_win32>, 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<bool isRequest, class Fields>
+ writer(header<isRequest, Fields>&, value_type& b)
+ : body_(b)
+ {
+ }
+
+ void
+ init(error_code&)
+ {
+ BOOST_ASSERT(body_.file_.is_open());
+ pos_ = body_.first_;
+ }
+
+ boost::optional<std::pair<const_buffers_type, bool>>
+ 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<bool isRequest, class Fields>
+ explicit
+ reader(header<isRequest, Fields>&, 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<class ConstBufferSequence>
+ 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<file_win32>::
+value_type::
+close()
+{
+ error_code ignored;
+ file_.close(ignored);
+}
+
+inline
+void
+basic_file_body<file_win32>::
+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<file_win32>::
+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<class Unsigned>
+boost::winapi::DWORD_
+lowPart(Unsigned n)
+{
+ return static_cast<
+ boost::winapi::DWORD_>(
+ n & 0xffffffff);
+}
+
+template<class Unsigned>
+boost::winapi::DWORD_
+highPart(Unsigned n, std::true_type)
+{
+ return static_cast<
+ boost::winapi::DWORD_>(
+ (n>>32)&0xffffffff);
+}
+
+template<class Unsigned>
+boost::winapi::DWORD_
+highPart(Unsigned, std::false_type)
+{
+ return 0;
+}
+
+template<class Unsigned>
+boost::winapi::DWORD_
+highPart(Unsigned n)
+{
+ return highPart(n, std::integral_constant<
+ bool, (sizeof(Unsigned)>4)>{});
+}
+
+class null_lambda
+{
+public:
+ template<class ConstBufferSequence>
+ 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<Handler, Executor>
+{
+ net::basic_stream_socket<
+ Protocol, Executor>& sock_;
+ serializer<isRequest,
+ basic_file_body<file_win32>, Fields>& sr_;
+ std::size_t bytes_transferred_ = 0;
+ bool header_ = false;
+
+public:
+ template<class Handler_>
+ write_some_win32_op(
+ Handler_&& h,
+ net::basic_stream_socket<
+ Protocol, Executor>& s,
+ serializer<isRequest,
+ basic_file_body<file_win32>,Fields>& sr)
+ : async_base<
+ Handler, Executor>(
+ std::forward<Handler_>(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<boost::winapi::DWORD_>(
+ (std::min<std::uint64_t>)(
+ (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr_.limit()),
+ (std::numeric_limits<boost::winapi::DWORD_>::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<int>(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<isRequest,
+ basic_file_body<file_win32>, 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<WriteHandler,
+ void(error_code, std::size_t)>::value,
+ "WriteHandler type requirements not met");
+
+ write_some_win32_op<
+ Protocol, Executor,
+ isRequest, Fields,
+ typename std::decay<WriteHandler>::type>(
+ std::forward<WriteHandler>(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<isRequest,
+ basic_file_body<file_win32>, 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<boost::winapi::DWORD_>(
+ (std::min<std::uint64_t>)(
+ (std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr.limit()),
+ (std::numeric_limits<boost::winapi::DWORD_>::max)()));
+ auto const bSuccess = ::TransmitFile(
+ sock.native_handle(),
+ w.body_.file_.native_handle(),
+ nNumberOfBytesToWrite,
+ 0,
+ nullptr,
+ nullptr,
+ 0);
+ if(! bSuccess)
+ {
+ ec.assign(static_cast<int>(
+ 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<isRequest,
+ basic_file_body<file_win32>, 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