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:
parent
b83d7da7a0
commit
14fea52dcb
3 changed files with 231 additions and 35 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
130
ircd/net.cc
130
ircd/net.cc
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue