sendfile_operation_win.ipp
Go to the documentation of this file.
/*
restinio
*/
#if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
#include <cstdio>
#include <restinio/utils/impl/safe_uint_truncate.hpp>
namespace restinio
{
namespace impl
{
namespace asio_details
{
#if RESTINIO_ASIO_VERSION < 101300
template<typename Socket >
decltype(auto)
executor_or_context_from_socket( Socket & socket )
{
return socket.get_executor().context();
}
#else
template<typename Socket >
decltype(auto)
executor_or_context_from_socket( Socket & socket )
{
return socket.get_executor();
}
#endif
} /* namespace asio_details */
//
// sendfile_operation_runner_t
//
template < typename Socket >
class sendfile_operation_runner_t final
: public sendfile_operation_runner_base_t< Socket >
{
public:
using base_type_t = sendfile_operation_runner_base_t< Socket >;
sendfile_operation_runner_t( const sendfile_operation_runner_t & ) = delete;
sendfile_operation_runner_t( sendfile_operation_runner_t && ) = delete;
sendfile_operation_runner_t & operator = ( const sendfile_operation_runner_t & ) = delete;
sendfile_operation_runner_t & operator = ( sendfile_operation_runner_t && ) = delete;
sendfile_operation_runner_t(
sendfile_t & sf,
default_asio_executor executor,
Socket & socket,
after_sendfile_cb_t after_sendfile_cb )
: base_type_t{ sf, std::move( executor), socket, std::move( after_sendfile_cb ) }
{
// We have passed sf.file_descriptor() to m_file_handle object.
// It means that file description will be closed automatically
// by m_file_handle.
// But sf still holds the same file_descriptor. Because of that
// we should tell sf to release this file_descriptor.
takeaway_file_descriptor(sf).release();
}
virtual void
start() override
{
init_next_read_some_from_file();
}
void
init_next_read_some_from_file() noexcept
{
const auto desired_size =
std::min< file_size_t >( this->m_remained_size, this->m_chunk_size );
try
{
this->m_file_handle.async_read_some_at(
this->m_next_write_offset,
asio_ns::buffer(
this->m_buffer.get(),
static_cast< std::size_t >( desired_size ) ),
asio_ns::bind_executor(
this->m_executor,
make_async_read_some_at_handler() ) );
}
catch( ... )
{
this->m_after_sendfile_cb(
make_asio_compaible_error(
asio_convertible_error_t::async_read_some_at_call_failed ),
this->m_transfered_size );
}
}
void
init_next_write( std::size_t len ) noexcept
{
try
{
asio_ns::async_write(
this->m_socket,
asio_ns::const_buffer{
this->m_buffer.get(),
static_cast< std::size_t >( len ) },
asio_ns::bind_executor(
this->m_executor,
make_async_write_handler() ) );
}
catch( ... )
{
this->m_after_sendfile_cb(
make_asio_compaible_error(
asio_convertible_error_t::async_write_call_failed ),
this->m_transfered_size );
}
}
private:
std::unique_ptr< char[] > m_buffer{ new char [ this->m_chunk_size ] };
asio_ns::windows::random_access_handle m_file_handle{
asio_details::executor_or_context_from_socket(this->m_socket),
this->m_file_descriptor
};
auto
make_async_read_some_at_handler() noexcept
{
return [this, ctx = this->shared_from_this()]
// NOTE: this lambda is noexcept since v.0.6.0.
( const asio_ns::error_code & ec, std::size_t len ) noexcept
{
if( ec || 0 == this->m_remained_size )
{
this->m_after_sendfile_cb( ec, this->m_transfered_size );
}
if( !ec )
{
if( 0 != len )
init_next_write( len );
else
{
this->m_after_sendfile_cb(
make_error_code( asio_ec::eof ),
this->m_transfered_size );
}
}
else
{
this->m_after_sendfile_cb( ec, this->m_transfered_size );
}
};
}
auto
make_async_write_handler() noexcept
{
return [ this, ctx = this->shared_from_this() ]
// NOTE: this lambda is noexcept since v.0.6.0.
( const asio_ns::error_code & ec, std::size_t written ) noexcept
{
if( !ec )
{
this->m_remained_size -= written;
this->m_transfered_size += written;
this->m_next_write_offset += written;
if( 0 == this->m_remained_size )
{
this->m_after_sendfile_cb( ec, this->m_transfered_size );
}
else
{
this->init_next_read_some_from_file();
}
}
else
{
this->m_after_sendfile_cb( ec, this->m_transfered_size );
}
};
}
};
template <>
class sendfile_operation_runner_t < asio_ns::ip::tcp::socket > final
: public sendfile_operation_runner_base_t< asio_ns::ip::tcp::socket >
{
auto
make_completion_handler() noexcept
{
return [this, ctx = shared_from_this() ]
// NOTE: this lambda is noexcept since v.0.6.0.
( const asio_ns::error_code & ec, std::size_t written )
{
if( !ec )
{
m_remained_size -= written;
m_transfered_size += written;
m_next_write_offset += written;
if( 0 == m_remained_size )
{
m_after_sendfile_cb( ec, m_transfered_size );
}
else
{
init_next_write();
}
}
else
{
m_after_sendfile_cb( ec, m_transfered_size );
}
};
}
public:
using base_type_t = sendfile_operation_runner_base_t< asio_ns::ip::tcp::socket >;
sendfile_operation_runner_t( const sendfile_operation_runner_t & ) = delete;
sendfile_operation_runner_t( sendfile_operation_runner_t && ) = delete;
sendfile_operation_runner_t & operator = ( const sendfile_operation_runner_t & ) = delete;
sendfile_operation_runner_t & operator = ( sendfile_operation_runner_t && ) = delete;
sendfile_operation_runner_t(
sendfile_t & sf,
default_asio_executor executor,
asio_ns::ip::tcp::socket & socket,
after_sendfile_cb_t after_sendfile_cb )
: base_type_t{ sf, std::move( executor), socket, std::move( after_sendfile_cb ) }
{
// We have passed sf.file_descriptor() to m_file_handle object.
// It means that file description will be closed automatically
// by m_file_handle.
// But sf still holds the same file_descriptor. Because of that
// we should tell sf to release this file_descriptor.
takeaway_file_descriptor(sf).release();
}
virtual void
start() override
{
init_next_write();
}
void
init_next_write() noexcept
{
// In this function bind_executor is the main suspect
// for throwing an exception. Because of that the whole
// function's logic is wrapped by try-catch.
try
{
asio_ns::windows::overlapped_ptr overlapped{
asio_details::executor_or_context_from_socket( m_socket ),
asio_ns::bind_executor(
m_executor,
make_completion_handler() )
};
// Set offset.
overlapped.get()->Offset =
static_cast< DWORD >( m_next_write_offset & 0xFFFFFFFFULL );
overlapped.get()->OffsetHigh =
static_cast< DWORD >( (m_next_write_offset>>32) & 0xFFFFFFFFULL );
// Amount of data to transfer.
const auto desired_size =
std::min< file_size_t >( this->m_remained_size, this->m_chunk_size );
// Initiate the TransmitFile operation.
BOOL ok =
::TransmitFile(
m_socket.native_handle(),
m_file_handle.native_handle(),
static_cast< DWORD >( desired_size ),
0,
overlapped.get(),
nullptr,
0 );
DWORD last_error = ::GetLastError();
// Check if the operation completed immediately.
if( !ok && last_error != ERROR_IO_PENDING )
{
// The operation completed immediately, so a completion notification needs
// to be posted. When complete() is called, ownership of the OVERLAPPED-
// derived object passes to the io_context.
overlapped.complete( make_error_code( last_error ) , 0 );
}
else
{
// The operation was successfully initiated, so ownership of the
// OVERLAPPED-derived object has passed to the io_context.
overlapped.release();
}
}
catch( ... )
{
// Report that error as a failure of async_write.
this->m_after_sendfile_cb(
make_asio_compaible_error(
asio_convertible_error_t::async_write_call_failed ),
this->m_transfered_size );
}
}
private:
std::unique_ptr< char[] > m_buffer{
std::make_unique< char[] >(
::restinio::utils::impl::uint64_to_size_t(
m_chunk_size ) )
};
asio_ns::windows::random_access_handle m_file_handle{
asio_details::executor_or_context_from_socket(m_socket),
m_file_descriptor
};
};
} /* namespace impl */
} /* namespace restinio */
#else // #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
#include "sendfile_operation_default.ipp"
#endif // #if defined(RESTINIO_ASIO_HAS_WINDOWS_OVERLAPPED_PTR)
restinio::impl::sendfile_operation_runner_base_t< asio_ns::ip::tcp::socket >
restinio::impl::sendfile_operation_runner_base_t::m_after_sendfile_cb
after_sendfile_cb_t m_after_sendfile_cb
Definition: sendfile_operation.hpp:83
restinio::impl::sendfile_operation_runner_base_t::m_chunk_size
const file_size_t m_chunk_size
Definition: sendfile_operation.hpp:77
restinio::impl::sendfile_operation_runner_base_t::m_remained_size
file_size_t m_remained_size
Definition: sendfile_operation.hpp:74
restinio::impl::sendfile_operation_runner_base_t::m_next_write_offset
file_offset_t m_next_write_offset
Definition: sendfile_operation.hpp:73
restinio::impl::sendfile_operation_runner_base_t::m_file_descriptor
file_descriptor_t m_file_descriptor
Definition: sendfile_operation.hpp:72
restinio::impl::sendfile_operation_runner_base_t::m_executor
default_asio_executor m_executor
Definition: sendfile_operation.hpp:81
restinio::impl::sendfile_operation_runner_base_t::m_socket
Socket & m_socket
Definition: sendfile_operation.hpp:82
restinio::impl::sendfile_operation_runner_base_t::m_transfered_size
file_size_t m_transfered_size
Definition: sendfile_operation.hpp:75
restinio::impl::sendfile_operation_runner_t::m_buffer
std::unique_ptr< char[] > m_buffer
Definition: sendfile_operation_default.ipp:108
restinio::impl::sendfile_operation_runner_t::make_async_write_handler
auto make_async_write_handler() noexcept
Helper method for making a lambda for async_write completion handler.
Definition: sendfile_operation_default.ipp:112
restinio::impl::sendfile_operation_runner_t::start
virtual void start() override
Definition: sendfile_operation_default.ipp:39
restinio::impl::sendfile_operation_runner_t::sendfile_operation_runner_t
sendfile_operation_runner_t(const sendfile_operation_runner_t &)=delete
restinio::impl::sendfile_operation_runner_t::base_type_t
sendfile_operation_runner_base_t< Socket > base_type_t
Definition: sendfile_operation_default.ipp:27
restinio::impl::sendfile_operation_runner_t::operator=
sendfile_operation_runner_t & operator=(const sendfile_operation_runner_t &)=delete
restinio::impl::sendfile_operation_runner_t::init_next_write
void init_next_write() noexcept
Definition: sendfile_operation_default.ipp:65
nonstd::optional_lite::std11::move
T & move(T &t)
Definition: optional.hpp:465
restinio::asio_ec::eof
constexpr auto eof
Definition: asio_include.hpp:41
restinio::impl::after_sendfile_cb_t
std::function< void(const asio_ns::error_code &, file_size_t) > after_sendfile_cb_t
Callback type for invocation when sendfile operation completes.
Definition: sendfile_operation.hpp:40
restinio::impl::make_error_code
auto make_error_code(const Error_Type &e) noexcept
Definition: sendfile_operation.hpp:88
restinio::status_code::ok
constexpr http_status_code_t ok
Definition: http_headers.hpp:2043
restinio::utils::impl::uint64_to_size_t
std::size_t uint64_to_size_t(std::uint64_t v)
Helper function for truncating uint64 to std::size_t with exception if that truncation will lead to d...
Definition: safe_uint_truncate.hpp:59
restinio
Definition: asio_include.hpp:21
restinio::default_asio_executor
asio_ns::executor default_asio_executor
Definition: asio_include.hpp:224
restinio::asio_convertible_error_t::async_read_some_at_call_failed
@ async_read_some_at_call_failed
A call to async_read_some_at failed. The corresponding sendfile operation wasn't done.
restinio::asio_convertible_error_t::async_write_call_failed
@ async_write_call_failed
A call to async_write failed. The corresponding write operation wasn't done.
restinio::const_buffer
constexpr const_buffer_t const_buffer(const void *str, std::size_t size) noexcept
Definition: buffers.hpp:424
restinio::make_asio_compaible_error
asio_ns::error_code make_asio_compaible_error(asio_convertible_error_t err) noexcept
Make restinio error_code compatible with asio_ns::error_code.
Definition: asio_include.hpp:210
std
STL namespace.
safe_uint_truncate.hpp
Helpers for safe truncation of unsigned integers.
sendfile_operation_default.ipp