123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651 |
- //
- // 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/buffers_range.hpp>
- #include <boost/beast/core/detail/clamp.hpp>
- #include <boost/beast/core/detail/is_invocable.hpp>
- #include <boost/beast/http/error.hpp>
- #include <boost/beast/http/write.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/error_codes.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;
- file_win32& file()
- {
- return file_;
- }
- bool
- is_open() const
- {
- return file_.is_open();
- }
- std::uint64_t
- size() const
- {
- return last_ - first_;
- }
- void
- close();
- void
- open(char const* path, file_mode mode, error_code& ec);
- void
- reset(file_win32&& file, error_code& ec);
- void
- seek(std::uint64_t offset, 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_[BOOST_BEAST_FILE_BUFFER_SIZE]; // 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)
- , pos_(body_.first_)
- {
- BOOST_ASSERT(body_.file_.is_open());
- }
- void
- init(error_code& ec)
- {
- BOOST_ASSERT(body_.file_.is_open());
- ec.clear();
- }
- 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;
- if (nread == 0)
- {
- BOOST_BEAST_ASSIGN_EC(ec, error::short_read);
- 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_ = file_.pos(ec);
- if(ec)
- {
- close();
- return;
- }
- last_ = size_;
- }
- }
- inline
- void
- basic_file_body<file_win32>::
- value_type::
- seek(std::uint64_t offset, error_code& ec)
- {
- first_ = offset;
- file_.seek(offset, ec);
- }
- //------------------------------------------------------------------------------
- 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);
- }
- };
- // https://github.com/boostorg/beast/issues/1815
- // developer commentary:
- // This function mimics the behaviour of ASIO.
- // Perhaps the correct fix is to insist on the use
- // of an appropriate error_condition to detect
- // connection_reset and connection_refused?
- inline
- error_code
- make_win32_error(
- boost::winapi::DWORD_ dwError) noexcept
- {
- // from
- // https://github.com/boostorg/asio/blob/6534af41b471288091ae39f9ab801594189b6fc9/include/boost/asio/detail/impl/socket_ops.ipp#L842
- switch(dwError)
- {
- case boost::winapi::ERROR_NETNAME_DELETED_:
- return net::error::connection_reset;
- case boost::winapi::ERROR_PORT_UNREACHABLE_:
- return net::error::connection_refused;
- case boost::winapi::WSAEMSGSIZE_:
- case boost::winapi::ERROR_MORE_DATA_:
- return {};
- }
- return error_code(
- static_cast<int>(dwError),
- system_category());
- }
- inline
- error_code
- make_win32_error(
- error_code ec) noexcept
- {
- if(ec.category() !=
- system_category())
- return ec;
- return make_win32_error(
- static_cast<boost::winapi::DWORD_>(
- ec.value()));
- }
- //------------------------------------------------------------------------------
- #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_;
- 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::INT_>::max)() - 1));
- 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(
- make_win32_error(dwError), 0);
- return;
- }
- overlapped.release();
- }
- void
- operator()(
- error_code ec,
- std::size_t bytes_transferred = 0)
- {
- if(ec)
- {
- BOOST_BEAST_ASSIGN_EC(ec, make_win32_error(ec));
- }
- else if(! ec && ! header_)
- {
- 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);
- }
- };
- template<class Protocol, class Executor>
- struct run_write_some_win32_op
- {
- net::basic_stream_socket<Protocol, Executor>* stream;
- using executor_type = typename net::basic_stream_socket<Protocol, Executor>::executor_type;
- executor_type
- get_executor() const noexcept
- {
- return stream->get_executor();
- }
- template<bool isRequest, class Fields, class WriteHandler>
- void
- operator()(
- WriteHandler&& h,
- 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), *stream, *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::INT_>::max)() - 1));
- auto const bSuccess = ::TransmitFile(
- sock.native_handle(),
- w.body_.file_.native_handle(),
- nNumberOfBytesToWrite,
- 0,
- nullptr,
- nullptr,
- 0);
- if(! bSuccess)
- {
- BOOST_BEAST_ASSIGN_EC(ec, detail::make_win32_error(
- boost::winapi::GetLastError()));
- 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,
- BOOST_BEAST_ASYNC_TPARAM2 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<Protocol, Executor>{&sock},
- handler,
- &sr);
- }
- #endif
- } // http
- } // beast
- } // boost
- #endif
- #endif
|