blob: c2ef09900979f576387a3eeff73fbf83418f9d3d [file] [log] [blame]
//
// detail/impl/winrt_ssocket_service_base.ipp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff 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)
//
#ifndef ASIO_DETAIL_IMPL_WINRT_SSOCKET_SERVICE_BASE_IPP
#define ASIO_DETAIL_IMPL_WINRT_SSOCKET_SERVICE_BASE_IPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#if defined(ASIO_WINDOWS_RUNTIME)
#include <cstring>
#include "asio/detail/winrt_ssocket_service_base.hpp"
#include "asio/detail/winrt_async_op.hpp"
#include "asio/detail/winrt_utils.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace detail {
winrt_ssocket_service_base::winrt_ssocket_service_base(
asio::io_context& io_context)
: io_context_(use_service<io_context_impl>(io_context)),
async_manager_(use_service<winrt_async_manager>(io_context)),
mutex_(),
impl_list_(0)
{
}
void winrt_ssocket_service_base::base_shutdown()
{
// Close all implementations, causing all operations to complete.
asio::detail::mutex::scoped_lock lock(mutex_);
base_implementation_type* impl = impl_list_;
while (impl)
{
asio::error_code ignored_ec;
close(*impl, ignored_ec);
impl = impl->next_;
}
}
void winrt_ssocket_service_base::construct(
winrt_ssocket_service_base::base_implementation_type& impl)
{
// Insert implementation into linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
impl.next_ = impl_list_;
impl.prev_ = 0;
if (impl_list_)
impl_list_->prev_ = &impl;
impl_list_ = &impl;
}
void winrt_ssocket_service_base::base_move_construct(
winrt_ssocket_service_base::base_implementation_type& impl,
winrt_ssocket_service_base::base_implementation_type& other_impl)
{
impl.socket_ = other_impl.socket_;
other_impl.socket_ = nullptr;
// Insert implementation into linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
impl.next_ = impl_list_;
impl.prev_ = 0;
if (impl_list_)
impl_list_->prev_ = &impl;
impl_list_ = &impl;
}
void winrt_ssocket_service_base::base_move_assign(
winrt_ssocket_service_base::base_implementation_type& impl,
winrt_ssocket_service_base& other_service,
winrt_ssocket_service_base::base_implementation_type& other_impl)
{
asio::error_code ignored_ec;
close(impl, ignored_ec);
if (this != &other_service)
{
// Remove implementation from linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
if (impl_list_ == &impl)
impl_list_ = impl.next_;
if (impl.prev_)
impl.prev_->next_ = impl.next_;
if (impl.next_)
impl.next_->prev_= impl.prev_;
impl.next_ = 0;
impl.prev_ = 0;
}
impl.socket_ = other_impl.socket_;
other_impl.socket_ = nullptr;
if (this != &other_service)
{
// Insert implementation into linked list of all implementations.
asio::detail::mutex::scoped_lock lock(other_service.mutex_);
impl.next_ = other_service.impl_list_;
impl.prev_ = 0;
if (other_service.impl_list_)
other_service.impl_list_->prev_ = &impl;
other_service.impl_list_ = &impl;
}
}
void winrt_ssocket_service_base::destroy(
winrt_ssocket_service_base::base_implementation_type& impl)
{
asio::error_code ignored_ec;
close(impl, ignored_ec);
// Remove implementation from linked list of all implementations.
asio::detail::mutex::scoped_lock lock(mutex_);
if (impl_list_ == &impl)
impl_list_ = impl.next_;
if (impl.prev_)
impl.prev_->next_ = impl.next_;
if (impl.next_)
impl.next_->prev_= impl.prev_;
impl.next_ = 0;
impl.prev_ = 0;
}
asio::error_code winrt_ssocket_service_base::close(
winrt_ssocket_service_base::base_implementation_type& impl,
asio::error_code& ec)
{
if (impl.socket_)
{
delete impl.socket_;
impl.socket_ = nullptr;
}
ec = asio::error_code();
return ec;
}
std::size_t winrt_ssocket_service_base::do_get_endpoint(
const base_implementation_type& impl, bool local,
void* addr, std::size_t addr_len, asio::error_code& ec) const
{
if (!is_open(impl))
{
ec = asio::error::bad_descriptor;
return addr_len;
}
try
{
std::string addr_string = winrt_utils::string(local
? impl.socket_->Information->LocalAddress->CanonicalName
: impl.socket_->Information->RemoteAddress->CanonicalName);
unsigned short port = winrt_utils::integer(local
? impl.socket_->Information->LocalPort
: impl.socket_->Information->RemotePort);
unsigned long scope = 0;
switch (reinterpret_cast<const socket_addr_type*>(addr)->sa_family)
{
case ASIO_OS_DEF(AF_INET):
if (addr_len < sizeof(sockaddr_in4_type))
{
ec = asio::error::invalid_argument;
return addr_len;
}
else
{
socket_ops::inet_pton(ASIO_OS_DEF(AF_INET), addr_string.c_str(),
&reinterpret_cast<sockaddr_in4_type*>(addr)->sin_addr, &scope, ec);
reinterpret_cast<sockaddr_in4_type*>(addr)->sin_port
= socket_ops::host_to_network_short(port);
ec = asio::error_code();
return sizeof(sockaddr_in4_type);
}
case ASIO_OS_DEF(AF_INET6):
if (addr_len < sizeof(sockaddr_in6_type))
{
ec = asio::error::invalid_argument;
return addr_len;
}
else
{
socket_ops::inet_pton(ASIO_OS_DEF(AF_INET6), addr_string.c_str(),
&reinterpret_cast<sockaddr_in6_type*>(addr)->sin6_addr, &scope, ec);
reinterpret_cast<sockaddr_in6_type*>(addr)->sin6_port
= socket_ops::host_to_network_short(port);
ec = asio::error_code();
return sizeof(sockaddr_in6_type);
}
default:
ec = asio::error::address_family_not_supported;
return addr_len;
}
}
catch (Platform::Exception^ e)
{
ec = asio::error_code(e->HResult,
asio::system_category());
return addr_len;
}
}
asio::error_code winrt_ssocket_service_base::do_set_option(
winrt_ssocket_service_base::base_implementation_type& impl,
int level, int optname, const void* optval,
std::size_t optlen, asio::error_code& ec)
{
if (!is_open(impl))
{
ec = asio::error::bad_descriptor;
return ec;
}
try
{
if (level == ASIO_OS_DEF(SOL_SOCKET)
&& optname == ASIO_OS_DEF(SO_KEEPALIVE))
{
if (optlen == sizeof(int))
{
int value = 0;
std::memcpy(&value, optval, optlen);
impl.socket_->Control->KeepAlive = !!value;
ec = asio::error_code();
}
else
{
ec = asio::error::invalid_argument;
}
}
else if (level == ASIO_OS_DEF(IPPROTO_TCP)
&& optname == ASIO_OS_DEF(TCP_NODELAY))
{
if (optlen == sizeof(int))
{
int value = 0;
std::memcpy(&value, optval, optlen);
impl.socket_->Control->NoDelay = !!value;
ec = asio::error_code();
}
else
{
ec = asio::error::invalid_argument;
}
}
else
{
ec = asio::error::invalid_argument;
}
}
catch (Platform::Exception^ e)
{
ec = asio::error_code(e->HResult,
asio::system_category());
}
return ec;
}
void winrt_ssocket_service_base::do_get_option(
const winrt_ssocket_service_base::base_implementation_type& impl,
int level, int optname, void* optval,
std::size_t* optlen, asio::error_code& ec) const
{
if (!is_open(impl))
{
ec = asio::error::bad_descriptor;
return;
}
try
{
if (level == ASIO_OS_DEF(SOL_SOCKET)
&& optname == ASIO_OS_DEF(SO_KEEPALIVE))
{
if (*optlen >= sizeof(int))
{
int value = impl.socket_->Control->KeepAlive ? 1 : 0;
std::memcpy(optval, &value, sizeof(int));
*optlen = sizeof(int);
ec = asio::error_code();
}
else
{
ec = asio::error::invalid_argument;
}
}
else if (level == ASIO_OS_DEF(IPPROTO_TCP)
&& optname == ASIO_OS_DEF(TCP_NODELAY))
{
if (*optlen >= sizeof(int))
{
int value = impl.socket_->Control->NoDelay ? 1 : 0;
std::memcpy(optval, &value, sizeof(int));
*optlen = sizeof(int);
ec = asio::error_code();
}
else
{
ec = asio::error::invalid_argument;
}
}
else
{
ec = asio::error::invalid_argument;
}
}
catch (Platform::Exception^ e)
{
ec = asio::error_code(e->HResult,
asio::system_category());
}
}
asio::error_code winrt_ssocket_service_base::do_connect(
winrt_ssocket_service_base::base_implementation_type& impl,
const void* addr, asio::error_code& ec)
{
if (!is_open(impl))
{
ec = asio::error::bad_descriptor;
return ec;
}
char addr_string[max_addr_v6_str_len];
unsigned short port;
switch (reinterpret_cast<const socket_addr_type*>(addr)->sa_family)
{
case ASIO_OS_DEF(AF_INET):
socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET),
&reinterpret_cast<const sockaddr_in4_type*>(addr)->sin_addr,
addr_string, sizeof(addr_string), 0, ec);
port = socket_ops::network_to_host_short(
reinterpret_cast<const sockaddr_in4_type*>(addr)->sin_port);
break;
case ASIO_OS_DEF(AF_INET6):
socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET6),
&reinterpret_cast<const sockaddr_in6_type*>(addr)->sin6_addr,
addr_string, sizeof(addr_string), 0, ec);
port = socket_ops::network_to_host_short(
reinterpret_cast<const sockaddr_in6_type*>(addr)->sin6_port);
break;
default:
ec = asio::error::address_family_not_supported;
return ec;
}
if (!ec) try
{
async_manager_.sync(impl.socket_->ConnectAsync(
ref new Windows::Networking::HostName(
winrt_utils::string(addr_string)),
winrt_utils::string(port)), ec);
}
catch (Platform::Exception^ e)
{
ec = asio::error_code(e->HResult,
asio::system_category());
}
return ec;
}
void winrt_ssocket_service_base::start_connect_op(
winrt_ssocket_service_base::base_implementation_type& impl,
const void* addr, winrt_async_op<void>* op, bool is_continuation)
{
if (!is_open(impl))
{
op->ec_ = asio::error::bad_descriptor;
io_context_.post_immediate_completion(op, is_continuation);
return;
}
char addr_string[max_addr_v6_str_len];
unsigned short port = 0;
switch (reinterpret_cast<const socket_addr_type*>(addr)->sa_family)
{
case ASIO_OS_DEF(AF_INET):
socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET),
&reinterpret_cast<const sockaddr_in4_type*>(addr)->sin_addr,
addr_string, sizeof(addr_string), 0, op->ec_);
port = socket_ops::network_to_host_short(
reinterpret_cast<const sockaddr_in4_type*>(addr)->sin_port);
break;
case ASIO_OS_DEF(AF_INET6):
socket_ops::inet_ntop(ASIO_OS_DEF(AF_INET6),
&reinterpret_cast<const sockaddr_in6_type*>(addr)->sin6_addr,
addr_string, sizeof(addr_string), 0, op->ec_);
port = socket_ops::network_to_host_short(
reinterpret_cast<const sockaddr_in6_type*>(addr)->sin6_port);
break;
default:
op->ec_ = asio::error::address_family_not_supported;
break;
}
if (op->ec_)
{
io_context_.post_immediate_completion(op, is_continuation);
return;
}
try
{
async_manager_.async(impl.socket_->ConnectAsync(
ref new Windows::Networking::HostName(
winrt_utils::string(addr_string)),
winrt_utils::string(port)), op);
}
catch (Platform::Exception^ e)
{
op->ec_ = asio::error_code(
e->HResult, asio::system_category());
io_context_.post_immediate_completion(op, is_continuation);
}
}
std::size_t winrt_ssocket_service_base::do_send(
winrt_ssocket_service_base::base_implementation_type& impl,
const asio::const_buffer& data,
socket_base::message_flags flags, asio::error_code& ec)
{
if (flags)
{
ec = asio::error::operation_not_supported;
return 0;
}
if (!is_open(impl))
{
ec = asio::error::bad_descriptor;
return 0;
}
try
{
buffer_sequence_adapter<asio::const_buffer,
asio::const_buffer> bufs(asio::buffer(data));
if (bufs.all_empty())
{
ec = asio::error_code();
return 0;
}
return async_manager_.sync(
impl.socket_->OutputStream->WriteAsync(bufs.buffers()[0]), ec);
}
catch (Platform::Exception^ e)
{
ec = asio::error_code(e->HResult,
asio::system_category());
return 0;
}
}
void winrt_ssocket_service_base::start_send_op(
winrt_ssocket_service_base::base_implementation_type& impl,
const asio::const_buffer& data, socket_base::message_flags flags,
winrt_async_op<unsigned int>* op, bool is_continuation)
{
if (flags)
{
op->ec_ = asio::error::operation_not_supported;
io_context_.post_immediate_completion(op, is_continuation);
return;
}
if (!is_open(impl))
{
op->ec_ = asio::error::bad_descriptor;
io_context_.post_immediate_completion(op, is_continuation);
return;
}
try
{
buffer_sequence_adapter<asio::const_buffer,
asio::const_buffer> bufs(asio::buffer(data));
if (bufs.all_empty())
{
io_context_.post_immediate_completion(op, is_continuation);
return;
}
async_manager_.async(
impl.socket_->OutputStream->WriteAsync(bufs.buffers()[0]), op);
}
catch (Platform::Exception^ e)
{
op->ec_ = asio::error_code(e->HResult,
asio::system_category());
io_context_.post_immediate_completion(op, is_continuation);
}
}
std::size_t winrt_ssocket_service_base::do_receive(
winrt_ssocket_service_base::base_implementation_type& impl,
const asio::mutable_buffer& data,
socket_base::message_flags flags, asio::error_code& ec)
{
if (flags)
{
ec = asio::error::operation_not_supported;
return 0;
}
if (!is_open(impl))
{
ec = asio::error::bad_descriptor;
return 0;
}
try
{
buffer_sequence_adapter<asio::mutable_buffer,
asio::mutable_buffer> bufs(asio::buffer(data));
if (bufs.all_empty())
{
ec = asio::error_code();
return 0;
}
async_manager_.sync(
impl.socket_->InputStream->ReadAsync(
bufs.buffers()[0], bufs.buffers()[0]->Capacity,
Windows::Storage::Streams::InputStreamOptions::Partial), ec);
std::size_t bytes_transferred = bufs.buffers()[0]->Length;
if (bytes_transferred == 0 && !ec)
{
ec = asio::error::eof;
}
return bytes_transferred;
}
catch (Platform::Exception^ e)
{
ec = asio::error_code(e->HResult,
asio::system_category());
return 0;
}
}
void winrt_ssocket_service_base::start_receive_op(
winrt_ssocket_service_base::base_implementation_type& impl,
const asio::mutable_buffer& data, socket_base::message_flags flags,
winrt_async_op<Windows::Storage::Streams::IBuffer^>* op,
bool is_continuation)
{
if (flags)
{
op->ec_ = asio::error::operation_not_supported;
io_context_.post_immediate_completion(op, is_continuation);
return;
}
if (!is_open(impl))
{
op->ec_ = asio::error::bad_descriptor;
io_context_.post_immediate_completion(op, is_continuation);
return;
}
try
{
buffer_sequence_adapter<asio::mutable_buffer,
asio::mutable_buffer> bufs(asio::buffer(data));
if (bufs.all_empty())
{
io_context_.post_immediate_completion(op, is_continuation);
return;
}
async_manager_.async(
impl.socket_->InputStream->ReadAsync(
bufs.buffers()[0], bufs.buffers()[0]->Capacity,
Windows::Storage::Streams::InputStreamOptions::Partial), op);
}
catch (Platform::Exception^ e)
{
op->ec_ = asio::error_code(e->HResult,
asio::system_category());
io_context_.post_immediate_completion(op, is_continuation);
}
}
} // namespace detail
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // defined(ASIO_WINDOWS_RUNTIME)
#endif // ASIO_DETAIL_IMPL_WINRT_SSOCKET_SERVICE_BASE_IPP