Files
boost_beast/include/boost/beast/http/impl/file_body_win32.ipp
Vinnie Falco f995fd41a3 net is a namespace alias for boost::asio:
The namespace alias beast::net replaces boost::asio in all code
and documentation.
2018-12-02 17:51:43 -08:00

601 lines
15 KiB
C++

//
// Copyright (c) 2016-2017 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_IPP
#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_IPP
#if BOOST_BEAST_USE_WIN32_FILE
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffers_range.hpp>
#include <boost/beast/core/type_traits.hpp>
#include <boost/beast/core/detail/clamp.hpp>
#include <boost/beast/http/serializer.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/basic_stream_socket.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.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 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>
friend class detail::write_some_win32_op;
template<
class Protocol, bool isRequest, class Fields>
friend
std::size_t
write_some(
net::basic_stream_socket<Protocol>& 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>
friend class detail::write_some_win32_op;
template<
class Protocol, bool isRequest, class Fields>
friend
std::size_t
write_some(
net::basic_stream_socket<Protocol>& 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.assign(0, ec.category());
return boost::none;
}
auto const nread = body_.file_.read(buf_, n, ec);
if(ec)
return boost::none;
BOOST_ASSERT(nread != 0);
pos_ += nread;
ec.assign(0, ec.category());
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.assign(0, ec.category());
}
template<class ConstBufferSequence>
std::size_t
put(ConstBufferSequence const& buffers,
error_code& ec)
{
std::size_t nwritten = 0;
for(auto buffer : beast::buffers_range(std::ref(buffers)))
{
nwritten += body_.file_.write(
buffer.data(), buffer.size(), ec);
if(ec)
return nwritten;
}
ec.assign(0, ec.category());
return nwritten;
}
void
finish(error_code& ec)
{
ec.assign(0, ec.category());
}
};
//--------------------------------------------------------------------------
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>
inline
boost::winapi::DWORD_
lowPart(Unsigned n)
{
return static_cast<
boost::winapi::DWORD_>(
n & 0xffffffff);
}
template<class Unsigned>
inline
boost::winapi::DWORD_
highPart(Unsigned n, std::true_type)
{
return static_cast<
boost::winapi::DWORD_>(
(n>>32)&0xffffffff);
}
template<class Unsigned>
inline
boost::winapi::DWORD_
highPart(Unsigned, std::false_type)
{
return 0;
}
template<class Unsigned>
inline
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 Handler,
bool isRequest, class Fields>
class write_some_win32_op
{
net::basic_stream_socket<Protocol>& sock_;
net::executor_work_guard<decltype(std::declval<
net::basic_stream_socket<Protocol>&>().get_executor())> wg_;
serializer<isRequest,
basic_file_body<file_win32>, Fields>& sr_;
std::size_t bytes_transferred_ = 0;
Handler h_;
bool header_ = false;
public:
write_some_win32_op(write_some_win32_op&&) = default;
write_some_win32_op(write_some_win32_op const&) = delete;
template<class DeducedHandler>
write_some_win32_op(
DeducedHandler&& h,
net::basic_stream_socket<Protocol>& s,
serializer<isRequest,
basic_file_body<file_win32>,Fields>& sr)
: sock_(s)
, wg_(sock_.get_executor())
, sr_(sr)
, h_(std::forward<DeducedHandler>(h))
{
}
using allocator_type =
net::associated_allocator_t<Handler>;
allocator_type
get_allocator() const noexcept
{
return (net::get_associated_allocator)(h_);
}
using executor_type =
net::associated_executor_t<Handler, decltype(std::declval<
net::basic_stream_socket<Protocol>&>().get_executor())>;
executor_type
get_executor() const noexcept
{
return (net::get_associated_executor)(
h_, sock_.get_executor());
}
void
operator()();
void
operator()(
error_code ec,
std::size_t bytes_transferred = 0);
friend
bool asio_handler_is_continuation(write_some_win32_op* op)
{
using net::asio_handler_is_continuation;
return asio_handler_is_continuation(
std::addressof(op->h_));
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, write_some_win32_op* op)
{
using net::asio_handler_invoke;
asio_handler_invoke(f, std::addressof(op->h_));
}
};
template<
class Protocol, class Handler,
bool isRequest, class Fields>
void
write_some_win32_op<
Protocol, Handler, isRequest, Fields>::
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().context(), 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();
}
template<
class Protocol, class Handler,
bool isRequest, class Fields>
void
write_some_win32_op<
Protocol, Handler, isRequest, Fields>::
operator()(
error_code ec, std::size_t bytes_transferred)
{
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());
}
}
h_(ec, bytes_transferred_);
}
#endif
} // detail
//------------------------------------------------------------------------------
template<class Protocol, bool isRequest, class Fields>
std::size_t
write_some(
net::basic_stream_socket<Protocol>& 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.assign(0, ec.category());
}
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,
bool isRequest, class Fields,
class WriteHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(
WriteHandler, void(error_code, std::size_t))
async_write_some(
net::basic_stream_socket<Protocol>& sock,
serializer<isRequest,
basic_file_body<file_win32>, Fields>& sr,
WriteHandler&& handler)
{
BOOST_BEAST_HANDLER_INIT(
WriteHandler, void(error_code, std::size_t));
detail::write_some_win32_op<
Protocol,
BOOST_ASIO_HANDLER_TYPE(WriteHandler,
void(error_code, std::size_t)),
isRequest, Fields>{
std::move(init.completion_handler), sock, sr}();
return init.result.get();
}
#endif
} // http
} // beast
} // boost
#endif
#endif