0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-25 08:12:37 +01:00

ircd::net: Add async overloads; various cleanup.

This commit is contained in:
Jason Volk 2017-10-19 03:55:24 -07:00
parent b83d7da7a0
commit 14fea52dcb
3 changed files with 231 additions and 35 deletions

View file

@ -33,6 +33,7 @@
namespace ircd::net
{
IRCD_EXCEPTION(ircd::error, error)
IRCD_EXCEPTION(error, nxdomain)
IRCD_EXCEPTION(error, broken_pipe)
IRCD_EXCEPTION(error, disconnected)
@ -48,8 +49,6 @@ namespace ircd::net
string_view string(const hostport &, const mutable_buffer &buf);
}
#include "listener.h"
// Public interface to socket.h because it is not included here.
namespace ircd::net
{
@ -69,6 +68,8 @@ namespace ircd::net
size_t read(socket &, iov<mutable_buffer> &); // read_some
}
#include "listener.h"
namespace ircd
{
using net::socket;

View file

@ -22,22 +22,19 @@
#pragma once
#define HAVE_IRCD_NET_SOCKET_H
/// This file is not included with the IRCd standard include stack because
/// it requires symbols we can't forward declare without boost headers. It
/// is part of the <ircd/asio.h> stack which can be included in your
/// definition file if you need low level access to this socket API. The
/// client.h still offers higher level access to sockets without requiring
/// boost headers; please check that for satisfaction before including this.
// This file is not included with the IRCd standard include stack because
// it requires symbols we can't forward declare without boost headers. It
// is part of the <ircd/asio.h> stack which can be included in your
// definition file if you need low level access to this socket API. The
// client.h still offers higher level access to sockets without requiring
// boost headers; please check that for satisfaction before including this.
namespace ircd::net
{
namespace ip = asio::ip;
using boost::system::error_code;
using asio::steady_timer;
IRCD_EXCEPTION(error, nxdomain)
struct socket;
extern asio::ssl::context sslv23_client;
@ -61,19 +58,10 @@ namespace ircd
struct ircd::net::socket
:std::enable_shared_from_this<ircd::net::socket>
{
struct io;
struct stat;
struct scope_timeout;
struct io;
enum dc
{
RST, // hardest disconnect
FIN, // graceful shutdown both directions
FIN_SEND, // graceful shutdown send side
FIN_RECV, // graceful shutdown recv side
SSL_NOTIFY, // SSL close_notify (async, errors ignored)
SSL_NOTIFY_YIELD, // SSL close_notify (yields context, throws)
};
enum class dc;
struct stat
{
@ -84,6 +72,7 @@ struct ircd::net::socket
using message_flags = boost::asio::socket_base::message_flags;
using handshake_type = asio::ssl::stream<ip::tcp::socket>::handshake_type;
using handler = std::function<void (const error_code &) noexcept>;
using xfer_handler = std::function<void (const error_code &, const size_t &) noexcept>;
asio::ssl::stream<ip::tcp::socket> ssl;
ip::tcp::socket &sd;
@ -97,50 +86,65 @@ struct ircd::net::socket
void handle(std::weak_ptr<socket>, handler, const error_code &, const size_t &) noexcept;
public:
// Getters for boost socket struct
operator const ip::tcp::socket &() const { return sd; }
operator ip::tcp::socket &() { return sd; }
// Observers
ip::tcp::endpoint remote() const { return sd.remote_endpoint(); }
ip::tcp::endpoint local() const { return sd.local_endpoint(); }
bool connected() const noexcept; // false on any sock errs
size_t available() const; // throws on errors; use friend variant for noex..
bool connected() const noexcept;
size_t available() const;
// low level read suite
template<class iov> auto read_some(const iov &, xfer_handler);
template<class iov> auto read_some(const iov &, error_code &);
template<class iov> auto read_some(const iov &);
template<class iov> auto read(const iov &, xfer_handler);
template<class iov> auto read(const iov &, error_code &);
template<class iov> auto read(const iov &);
// low level write suite
template<class iov> auto write_some(const iov &, xfer_handler);
template<class iov> auto write_some(const iov &, error_code &);
template<class iov> auto write_some(const iov &);
template<class iov> auto write(const iov &, xfer_handler);
template<class iov> auto write(const iov &, error_code &);
template<class iov> auto write(const iov &);
// Timer for this socket
void set_timeout(const milliseconds &, handler);
void set_timeout(const milliseconds &);
error_code cancel_timeout() noexcept;
// Asynchronous 'ready' closure
// Asynchronous callback when socket ready for read
void operator()(const milliseconds &timeout, handler);
void operator()(handler);
bool cancel() noexcept;
void disconnect(const dc &type = dc::SSL_NOTIFY);
// Connect to host; synchronous (yield) and asynchronous (callback) variants
void connect(const ip::tcp::endpoint &ep, const milliseconds &timeout, handler callback);
void connect(const ip::tcp::endpoint &ep, const milliseconds &timeout = -1ms);
void disconnect(const dc &type);
// Construct, resolve and connect client socket to remote host (yields)
socket(const std::string &host,
const uint16_t &port,
const milliseconds &timeout = -1ms,
asio::ssl::context &ssl = sslv23_client,
boost::asio::io_service *const &ios = ircd::ios);
// Construct and connect client socket to remote host (yields)
socket(const ip::tcp::endpoint &remote,
const milliseconds &timeout = -1ms,
asio::ssl::context &ssl = sslv23_client,
boost::asio::io_service *const &ios = ircd::ios);
// Construct socket only
socket(asio::ssl::context &ssl = sslv23_client,
boost::asio::io_service *const &ios = ircd::ios);
// Socket cannot be copied or moved; must be constructed as shared ptr
socket(socket &&) = delete;
socket(const socket &) = delete;
~socket() noexcept;
@ -148,12 +152,18 @@ struct ircd::net::socket
class ircd::net::socket::scope_timeout
{
socket *s;
socket *s {nullptr};
public:
scope_timeout(socket &, const milliseconds &timeout, const socket::handler &handler);
bool cancel() noexcept; // invoke timer.cancel() before dtor
bool release(); // cancels the cancel;
scope_timeout(socket &, const milliseconds &timeout, socket::handler handler);
scope_timeout(socket &, const milliseconds &timeout);
scope_timeout() = default;
scope_timeout(scope_timeout &&) noexcept;
scope_timeout(const scope_timeout &) = delete;
scope_timeout &operator=(scope_timeout &&) noexcept;
scope_timeout &operator=(const scope_timeout &) = delete;
~scope_timeout() noexcept;
};
@ -167,9 +177,20 @@ class ircd::net::socket::io
public:
operator size_t() const;
io(struct socket &, struct stat &, const size_t &bytes);
io(struct socket &, struct stat &, const std::function<size_t ()> &closure);
};
enum class ircd::net::socket::dc
{
RST, // hardest disconnect
FIN, // graceful shutdown both directions
FIN_SEND, // graceful shutdown send side
FIN_RECV, // graceful shutdown recv side
SSL_NOTIFY, // SSL close_notify (async, errors ignored)
SSL_NOTIFY_YIELD, // SSL close_notify (yields context, throws)
};
template<class iov>
auto
ircd::net::socket::write(const iov &bufs)
@ -191,6 +212,20 @@ ircd::net::socket::write(const iov &bufs,
});
}
template<class iov>
auto
ircd::net::socket::write(const iov &bufs,
xfer_handler handler)
{
async_write(ssl, bufs, asio::transfer_all(), [this, handler(std::move(handler))]
(const error_code &ec, const size_t &bytes)
noexcept
{
io{*this, out, bytes};
handler(ec, bytes);
});
}
template<class iov>
auto
ircd::net::socket::write_some(const iov &bufs)
@ -212,6 +247,20 @@ ircd::net::socket::write_some(const iov &bufs,
});
}
template<class iov>
auto
ircd::net::socket::write_some(const iov &bufs,
xfer_handler handler)
{
ssl.async_write_some(bufs, [this, handler(std::move(handler))]
(const error_code &ec, const size_t &bytes)
noexcept
{
io{*this, out, bytes};
handler(ec, bytes);
});
}
template<class iov>
auto
ircd::net::socket::read(const iov &bufs)
@ -238,6 +287,20 @@ ircd::net::socket::read(const iov &bufs,
});
}
template<class iov>
auto
ircd::net::socket::read(const iov &bufs,
xfer_handler handler)
{
async_read(ssl, bufs, [this, handler(std::move(handler))]
(const error_code &ec, const size_t &bytes)
noexcept
{
io{*this, in, bytes};
handler(ec, bytes);
});
}
template<class iov>
auto
ircd::net::socket::read_some(const iov &bufs)
@ -263,3 +326,17 @@ ircd::net::socket::read_some(const iov &bufs,
return ssl.async_read_some(bufs, yield_context{to_asio{}}[ec]);
});
}
template<class iov>
auto
ircd::net::socket::read_some(const iov &bufs,
xfer_handler handler)
{
ssl.async_read_some(bufs, [this, handler(std::move(handler))]
(const error_code &ec, const size_t &bytes)
noexcept
{
io{*this, in, bytes};
handler(ec, bytes);
});
}

View file

@ -31,11 +31,15 @@ namespace ircd::net
ip::tcp::resolver *resolver;
}
/// Network subsystem initialization
///
ircd::net::init::init()
{
net::resolver = new ip::tcp::resolver{*ircd::ios};
}
/// Network subsystem shutdown
///
ircd::net::init::~init()
{
assert(net::resolver);
@ -44,7 +48,7 @@ ircd::net::init::~init()
}
//
// net.h
// host / port utils
//
std::string
@ -67,6 +71,10 @@ ircd::net::string(const hostport &pair,
return { data(buf), size_t(len) };
}
//
// socket (public)
//
size_t
ircd::net::read(socket &socket,
iov<mutable_buffer> &bufs)
@ -561,9 +569,18 @@ ircd::net::address(const ip::tcp::endpoint &ep)
ircd::net::socket::io::io(struct socket &sock,
struct stat &stat,
const std::function<size_t ()> &closure)
:io
{
sock, stat, closure()
}
{}
ircd::net::socket::io::io(struct socket &sock,
struct stat &stat,
const size_t &bytes)
:sock{sock}
,stat{stat}
,bytes{closure()}
,bytes{bytes}
{
stat.bytes += bytes;
stat.calls++;
@ -593,16 +610,69 @@ ircd::net::socket::scope_timeout::scope_timeout(socket &socket,
ircd::net::socket::scope_timeout::scope_timeout(socket &socket,
const milliseconds &timeout,
const socket::handler &handler)
socket::handler handler)
:s{&socket}
{
socket.set_timeout(timeout, handler);
socket.set_timeout(timeout, std::move(handler));
}
ircd::net::socket::scope_timeout::scope_timeout(scope_timeout &&other)
noexcept
:s{std::move(other.s)}
{
other.s = nullptr;
}
ircd::net::socket::scope_timeout &
ircd::net::socket::scope_timeout::operator=(scope_timeout &&other)
noexcept
{
this->~scope_timeout();
s = std::move(other.s);
return *this;
}
ircd::net::socket::scope_timeout::~scope_timeout()
noexcept
noexcept try
{
if(s)
s->timer.cancel();
}
catch(const std::exception &e)
{
log::error("socket(%p) ~scope_timeout: %s",
(const void *)s,
e.what());
return;
}
bool
ircd::net::socket::scope_timeout::cancel()
noexcept try
{
if(!this->s)
return false;
auto *const s{this->s};
this->s = nullptr;
s->timer.cancel();
return true;
}
catch(const std::exception &e)
{
log::error("socket(%p) scope_timeout::cancel: %s",
(const void *)s,
e.what());
return false;
}
bool
ircd::net::socket::scope_timeout::release()
{
const auto s{this->s};
this->s = nullptr;
return s != nullptr;
}
//
@ -703,6 +773,8 @@ catch(const std::exception &e)
return;
}
/// Attempt to connect and ssl handshake remote; yields ircd::ctx; throws timeout
///
void
ircd::net::socket::connect(const ip::tcp::endpoint &ep,
const milliseconds &timeout)
@ -712,6 +784,43 @@ ircd::net::socket::connect(const ip::tcp::endpoint &ep,
ssl.async_handshake(socket::handshake_type::client, yield_context{to_asio{}});
}
/// Attempt to connect and ssl handshake; asynchronous, callback when done.
///
void
ircd::net::socket::connect(const ip::tcp::endpoint &ep,
const milliseconds &timeout,
handler callback)
{
auto handshake_handler{[this, callback(std::move(callback))]
(const error_code &ec)
noexcept
{
if(!timedout)
cancel_timeout();
else
assert(ec == boost::system::errc::operation_canceled);
callback(ec);
}};
auto connect_handler{[this, handshake_handler(std::move(handshake_handler))]
(const error_code &ec)
noexcept
{
if(ec)
{
handshake_handler(ec);
return;
}
static const auto handshake{socket::handshake_type::client};
ssl.async_handshake(handshake, std::move(handshake_handler));
}};
set_timeout(timeout);
sd.async_connect(ep, std::move(connect_handler));
}
void
ircd::net::socket::disconnect(const dc &type)
{
@ -980,6 +1089,15 @@ catch(const boost::system::system_error &e)
return false;
}
ircd::net::error_code
ircd::net::socket::cancel_timeout()
noexcept
{
boost::system::error_code ec;
timer.cancel(ec);
return ec;
}
void
ircd::net::socket::set_timeout(const milliseconds &t)
{
@ -992,7 +1110,7 @@ ircd::net::socket::set_timeout(const milliseconds &t)
void
ircd::net::socket::set_timeout(const milliseconds &t,
handler h)
handler h)
{
if(t < milliseconds(0))
return;