0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-09-27 11:18:51 +02:00

ircd::net: Various net/client/listener bugfixes/cleanup.

This commit is contained in:
Jason Volk 2017-11-01 15:51:24 -07:00
parent 20961bffa6
commit 2930b93dcb
8 changed files with 423 additions and 193 deletions

View file

@ -64,6 +64,7 @@ struct ircd::strand
///
#include <ircd/ctx/continuation.h>
#include <ircd/net/asio.h>
#include <ircd/net/socket.h>
inline ircd::strand::operator

View file

@ -71,6 +71,8 @@ struct ircd::client
struct ircd::client::init
{
void interrupt();
init();
~init() noexcept;
};

56
include/ircd/net/asio.h Normal file
View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#define HAVE_IRCD_NET_ASIO_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
// <ircd/net/net.h> still offers higher level access to sockets without
// requiring boost headers; please check that for satisfaction before
// including this.
namespace ircd::net
{
using boost::system::error_code;
string_view string(const mutable_buffer &, const boost::system::error_code &);
string_view string(const mutable_buffer &, const boost::system::system_error &);
std::string string(const boost::system::error_code &);
std::string string(const boost::system::system_error &);
namespace ip = asio::ip;
uint16_t port(const ip::tcp::endpoint &);
ip::address addr(const ip::tcp::endpoint &);
std::string host(const ip::tcp::endpoint &);
std::string string(const ip::address &);
std::string string(const ip::tcp::endpoint &);
}
namespace ircd
{
using net::error_code;
using net::string;
using net::addr;
using net::host;
using net::port;
}

View file

@ -34,7 +34,7 @@ struct ircd::net::listener
IRCD_EXCEPTION(ircd::error, error)
private:
std::unique_ptr<struct acceptor> acceptor;
std::shared_ptr<struct acceptor> acceptor;
public:
listener(const json::object &options);

View file

@ -31,31 +31,15 @@
namespace ircd::net
{
namespace ip = asio::ip;
using boost::system::error_code;
using asio::steady_timer;
struct socket;
extern asio::ssl::context sslv23_client;
uint16_t port(const ip::tcp::endpoint &);
ip::address addr(const ip::tcp::endpoint &);
std::string host(const ip::tcp::endpoint &);
std::string string(const ip::address &);
std::string string(const ip::tcp::endpoint &);
std::shared_ptr<socket> connect(const ip::tcp::endpoint &remote, const milliseconds &timeout);
}
namespace ircd
{
using net::error_code;
using net::string;
using net::addr;
using net::host;
using net::port;
}
struct ircd::net::socket
:std::enable_shared_from_this<ircd::net::socket>
{
@ -78,7 +62,7 @@ struct ircd::net::socket
asio::ssl::stream<ip::tcp::socket &> ssl;
steady_timer timer;
stat in, out;
bool timedout;
bool timedout {false};
void call_user(const handler &, const error_code &) noexcept;
bool handle_error(const error_code &ec);

View file

@ -207,11 +207,11 @@ ircd::client::client(std::shared_ptr<socket> sock)
ircd::client::~client()
noexcept try
{
disconnect(*this, net::dc::SSL_NOTIFY);
//assert(!sock || !connected(*sock));
}
catch(const std::exception &e)
{
log::error("~client(%p): %s", this, e.what());
log::critical("~client(%p): %s", this, e.what());
return;
}
@ -259,12 +259,20 @@ catch(const boost::system::system_error &e)
if(ec.category() == get_system_category()) switch(ec.value())
{
case success:
assert(0);
return true;
case broken_pipe:
case connection_reset:
case not_connected:
disconnect(*this, net::dc::RST);
return false;
case operation_canceled:
disconnect(*this, net::dc::SSL_NOTIFY);
return false;
case bad_file_descriptor:
return false;
default:
@ -273,6 +281,7 @@ catch(const boost::system::system_error &e)
else if(ec.category() == get_misc_category()) switch(ec.value())
{
case boost::asio::error::eof:
disconnect(*this, net::dc::RST);
return false;
default:
@ -281,19 +290,20 @@ catch(const boost::system::system_error &e)
else if(ec.category() == get_ssl_category()) switch(ec.value())
{
case SSL_R_SHORT_READ:
disconnect(*this, net::dc::RST);
return false;
default:
break;
}
log::critical("client(%p): (unexpected) system_error: %s",
(const void *)this,
e.what());
if(ircd::debugmode)
throw;
log::error("client(%p): (unexpected) %s: (%d) %s",
(const void *)this,
ec.category().name(),
int{ec.value()},
ec.message());
disconnect(*this, net::dc::RST);
return false;
}
catch(const std::exception &e)
@ -302,10 +312,11 @@ catch(const std::exception &e)
string(remote(*this)),
e.what());
if(ircd::debugmode)
#ifdef RB_DEBUG
throw;
return false;
#else
return false;
#endif
}
bool
@ -314,12 +325,15 @@ ircd::handle_request(client &client,
try
{
client.request_timer = ircd::timer{};
client.sock->set_timeout(request_timeout, [client(shared_from(client))]
(const net::error_code &ec)
const socket::scope_timeout timeout
{
if(!ec)
disconnect(*client, net::dc::SSL_NOTIFY_YIELD);
});
*client.sock, request_timeout, [client(shared_from(client))]
(const net::error_code &ec)
{
if(!ec)
disconnect(*client, net::dc::SSL_NOTIFY_YIELD);
}
};
bool ret{true};
http::request
@ -327,7 +341,6 @@ try
pc, nullptr, write_closure(client), [&client, &pc, &ret]
(const auto &head)
{
client.sock->timer.cancel();
handle_request(client, pc, head);
ret = !iequals(head.connection, "close"s);
}
@ -400,13 +413,18 @@ ircd::make_client(args&&... a)
void
ircd::disconnect_all()
{
for(auto &client : client::clients) try
auto it(begin(client::clients));
while(it != end(client::clients))
{
disconnect(*client, net::dc::RST);
}
catch(const std::exception &e)
{
log::warning("Error disconnecting client @%p: %s", &client, e.what());
auto *const client(*it);
++it; try
{
disconnect(*client, net::dc::SSL_NOTIFY);
}
catch(const std::exception &e)
{
log::warning("Error disconnecting client @%p: %s", client, e.what());
}
}
}
@ -443,10 +461,13 @@ void
ircd::async_recv_next(std::shared_ptr<client> client,
const milliseconds &timeout)
{
auto &sock(*client->sock);
assert(bool(client));
assert(bool(client->sock));
// This call returns immediately so we no longer block the current context and
// its stack while waiting for activity on idle connections between requests.
auto &sock(*client->sock);
sock(timeout, [client(std::move(client)), timeout](const net::error_code &ec)
noexcept
{
@ -459,14 +480,16 @@ ircd::async_recv_next(std::shared_ptr<client> client,
// of the ircd::context system. The context the closure ends up getting is the next
// available from the request pool, which may not be available immediately so this
// handler might be queued for some time after this call returns.
request([ec, client, timeout]
request([ec, client(std::move(client)), timeout]
{
// Right here this handler is executing on an ircd::context with its own
// stack dedicated to the lifetime of this request. If client::main()
// returns true, we bring the client back into async mode to wait for
// the next request.
if(client->main())
async_recv_next(client, timeout);
async_recv_next(std::move(client), timeout);
else
disconnect(*client, net::dc::SSL_NOTIFY_YIELD);
});
});
}
@ -525,17 +548,17 @@ bool
ircd::handle_ec_short_read(client &client)
try
{
log::debug("client[%s]: short_read",
string(remote(client)));
log::warning("client[%s]: short_read",
string(remote(client)));
disconnect(client, net::dc::RST);
return false;
}
catch(const std::exception &e)
{
log::warning("client(%p): short_read: %s",
&client,
e.what());
log::error("client(%p): short_read: %s",
&client,
e.what());
return false;
}
@ -552,9 +575,9 @@ try
}
catch(const std::exception &e)
{
log::warning("client(%p): EOF: %s",
&client,
e.what());
log::error("client(%p): EOF: %s",
&client,
e.what());
return false;
}
@ -564,17 +587,17 @@ ircd::handle_ec_timeout(client &client)
try
{
assert(bool(client.sock));
log::debug("client[%s]: disconnecting after inactivity timeout",
string(remote(client)));
log::warning("client[%s]: disconnecting after inactivity timeout",
string(remote(client)));
disconnect(client, net::dc::SSL_NOTIFY);
return false;
}
catch(const std::exception &e)
{
log::warning("client(%p): timeout: %s",
&client,
e.what());
log::error("client(%p): timeout: %s",
&client,
e.what());
return false;
}

View file

@ -204,6 +204,12 @@ try
js::init _js_; // SpiderMonkey
m::init _matrix_; // Matrix
// Any deinits which have to be done with all subsystems intact
const unwind shutdown{[&]
{
_client_.interrupt();
}};
// IRCd will now transition to the RUN state indicating full functionality.
ircd::set_runlevel(runlevel::RUN);
@ -257,10 +263,14 @@ try
if(ircd::runlevel_changed)
ios->post([new_runlevel]
{
if(new_runlevel == runlevel::STOPPED)
log::notice("IRCd %s", reflect(new_runlevel));
ircd::runlevel_changed(new_runlevel);
});
log::notice("IRCd %s", reflect(ircd::runlevel));
if(new_runlevel != runlevel::STOPPED)
log::notice("IRCd %s", reflect(new_runlevel));
}
catch(const std::exception &e)
{

View file

@ -125,30 +125,8 @@ ircd::net::write(socket &socket,
// net/listener.h
//
//
// ircd::net::listener
//
ircd::net::listener::listener(const std::string &opts)
:listener{json::object{opts}}
{
}
ircd::net::listener::listener(const json::object &opts)
:acceptor{std::make_unique<struct acceptor>(opts)}
{
}
ircd::net::listener::~listener()
noexcept
{
}
//
// ircd::net::listener::acceptor
//
struct ircd::net::listener::acceptor
:std::enable_shared_from_this<struct ircd::net::listener::acceptor>
{
using error_code = boost::system::error_code;
@ -159,25 +137,94 @@ struct ircd::net::listener::acceptor
asio::ssl::context ssl;
ip::tcp::endpoint ep;
ip::tcp::acceptor a;
size_t accepting {0};
size_t handshaking {0};
bool interrupting {false};
ctx::dock joining;
explicit operator std::string() const;
void configure(const json::object &opts);
// Handshake stack
bool handshake_error(const error_code &ec);
void handshake(const error_code &ec, std::shared_ptr<socket>) noexcept;
bool handshake_error(const error_code &ec, socket &);
void handshake(const error_code &ec, std::shared_ptr<socket>, std::weak_ptr<acceptor>) noexcept;
// Acceptance stack
bool accept_error(const error_code &ec);
void accept(const error_code &ec, std::shared_ptr<socket>) noexcept;
bool accept_error(const error_code &ec, socket &);
void accept(const error_code &ec, std::shared_ptr<socket>, std::weak_ptr<acceptor>) noexcept;
// Accept next
void next();
// Acceptor shutdown
bool interrupt() noexcept;
void join() noexcept;
acceptor(const json::object &opts);
~acceptor() noexcept;
};
//
// ircd::net::listener
//
ircd::net::listener::listener(const std::string &opts)
:listener{json::object{opts}}
{
}
ircd::net::listener::listener(const json::object &opts)
:acceptor{std::make_shared<struct acceptor>(opts)}
{
// Starts the first asynchronous accept. This has to be done out here after
// the acceptor's shared object is constructed.
acceptor->next();
}
/// Cancels all pending accepts and handshakes and waits (yields ircd::ctx)
/// until report.
///
ircd::net::listener::~listener()
noexcept
{
if(acceptor)
acceptor->join();
}
void
ircd::net::listener::acceptor::join()
noexcept try
{
interrupt();
joining.wait([this]
{
return !accepting && !handshaking;
});
}
catch(const std::exception &e)
{
log.error("acceptor(%p): join: %s",
this,
e.what());
}
bool
ircd::net::listener::acceptor::interrupt()
noexcept try
{
a.cancel();
interrupting = true;
return true;
}
catch(const boost::system::system_error &e)
{
log.error("acceptor(%p): interrupt: %s",
this,
string(e));
return false;
}
//
// ircd::net::listener::acceptor
//
@ -243,8 +290,6 @@ try
std::string(*this),
backlog,
max_connections);
next();
}
catch(const boost::system::system_error &e)
{
@ -254,9 +299,13 @@ catch(const boost::system::system_error &e)
ircd::net::listener::acceptor::~acceptor()
noexcept
{
a.cancel();
}
/// Sets the next asynchronous handler to start the next accept sequence.
/// Each call to next() sets one handler which handles the connect for one
/// socket. After the connect, an asynchronous SSL handshake handler is set
/// for the socket, and next() is called again to setup for the next socket
/// too.
void
ircd::net::listener::acceptor::next()
try
@ -266,9 +315,9 @@ try
std::string(*this),
sock.get());
// The context blocks here until the next client is connected.
ip::tcp::socket &sd(*sock);
a.async_accept(sd, std::bind(&acceptor::accept, this, ph::_1, sock));
a.async_accept(sd, std::bind(&acceptor::accept, this, ph::_1, sock, weak_from(*this)));
++accepting;
}
catch(const std::exception &e)
{
@ -280,19 +329,37 @@ catch(const std::exception &e)
throw;
}
/// Callback for a socket connected. This handler then invokes the
/// asynchronous SSL handshake sequence.
///
void
ircd::net::listener::acceptor::accept(const error_code &ec,
const std::shared_ptr<socket> sock)
const std::shared_ptr<socket> sock,
const std::weak_ptr<acceptor> a)
noexcept try
{
if(accept_error(ec))
if(unlikely(a.expired()))
return;
const unwind next{[this]
--accepting;
const unwind::nominal next{[this]
{
this->next();
}};
const unwind::exceptional drop{[&sock]
{
assert(bool(sock));
disconnect(*sock, dc::RST);
}};
assert(bool(sock));
if(unlikely(accept_error(ec, *sock)))
{
disconnect(*sock, dc::RST);
return;
}
log.debug("%s: socket(%p) accepted %s",
std::string(*this),
sock.get(),
@ -315,24 +382,43 @@ noexcept try
auto handshake
{
std::bind(&acceptor::handshake, this, ph::_1, sock)
std::bind(&acceptor::handshake, this, ph::_1, sock, a)
};
sock->ssl.async_handshake(handshake_type, std::move(handshake));
++handshaking;
}
catch(const ctx::interrupted &e)
{
log.debug("%s: acceptor interrupted socket(%p): %s",
std::string(*this),
sock.get(),
string(ec));
joining.notify_all();
}
catch(const std::exception &e)
{
log.error("%s: socket(%p) in accept(): %s",
log.error("%s: socket(%p): in accept(): [%s]: %s",
std::string(*this),
sock.get(),
sock->connected()? string(sock->remote()) : "<gone>",
e.what());
}
/// Error handler for the accept socket callback. This handler determines
/// whether or not the handler should return or continue processing the
/// result.
///
bool
ircd::net::listener::acceptor::accept_error(const error_code &ec)
ircd::net::listener::acceptor::accept_error(const error_code &ec,
socket &sock)
{
using boost::system::get_system_category;
if(unlikely(interrupting))
throw ctx::interrupted();
if(ec.category() == get_system_category()) switch(ec.value())
{
using namespace boost::system::errc;
@ -341,7 +427,7 @@ ircd::net::listener::acceptor::accept_error(const error_code &ec)
return false;
case operation_canceled:
return true;
return false;
default:
break;
@ -352,33 +438,64 @@ ircd::net::listener::acceptor::accept_error(const error_code &ec)
void
ircd::net::listener::acceptor::handshake(const error_code &ec,
const std::shared_ptr<socket> sock)
const std::shared_ptr<socket> sock,
const std::weak_ptr<acceptor> a)
noexcept try
{
if(handshake_error(ec))
if(unlikely(a.expired()))
return;
log.debug("%s socket(%p) SSL handshook %s",
--handshaking;
assert(bool(sock));
const unwind::exceptional drop{[&sock]
{
disconnect(*sock, dc::RST);
}};
if(unlikely(handshake_error(ec, *sock)))
{
disconnect(*sock, dc::RST);
return;
}
log.debug("%s socket(%p): SSL handshook %s",
std::string(*this),
sock.get(),
string(sock->remote()));
add_client(sock);
}
catch(const ctx::interrupted &e)
{
log.debug("%s: SSL handshake interrupted socket(%p): %s",
std::string(*this),
sock.get(),
string(ec));
joining.notify_all();
}
catch(const std::exception &e)
{
log.error("%s: socket(%p) in handshake(): [%s]: %s",
log.error("%s: socket(%p): in handshake(): [%s]: %s",
std::string(*this),
sock.get(),
sock->connected()? string(sock->remote()) : "<gone>",
e.what());
}
/// Error handler for the SSL handshake callback. This handler determines
/// whether or not the handler should return or continue processing the
/// result.
///
bool
ircd::net::listener::acceptor::handshake_error(const error_code &ec)
ircd::net::listener::acceptor::handshake_error(const error_code &ec,
socket &sock)
{
using boost::system::get_system_category;
if(unlikely(interrupting))
throw ctx::interrupted();
if(ec.category() == get_system_category()) switch(ec.value())
{
using namespace boost::system::errc;
@ -387,7 +504,7 @@ ircd::net::listener::acceptor::handshake_error(const error_code &ec)
return false;
case operation_canceled:
return true;
return false;
default:
break;
@ -483,17 +600,10 @@ ircd::net::listener::acceptor::configure(const json::object &opts)
ircd::net::listener::acceptor::operator std::string()
const
{
std::string ret(256, char{});
const auto length
return fmt::snstringf
{
fmt::sprintf(mutable_buffer{ret}, "'%s' @ [%s]:%u",
name,
string(ep.address()),
ep.port())
256, "'%s' @ [%s]:%u", name, string(ep.address()), ep.port()
};
ret.resize(length);
return ret;
}
///////////////////////////////////////////////////////////////////////////////
@ -591,40 +701,6 @@ noexcept
return s.connected();
}
uint16_t
ircd::net::port(const ip::tcp::endpoint &ep)
{
return ep.port();
}
std::string
ircd::net::host(const ip::tcp::endpoint &ep)
{
return string(addr(ep));
}
std::string
ircd::net::string(const ip::address &addr)
{
return addr.to_string();
}
std::string
ircd::net::string(const ip::tcp::endpoint &ep)
{
std::string ret(256, char{});
const auto addr{string(net::addr(ep))};
const auto data{const_cast<char *>(ret.data())};
ret.resize(snprintf(data, ret.size(), "%s:%u", addr.c_str(), port(ep)));
return ret;
}
boost::asio::ip::address
ircd::net::addr(const ip::tcp::endpoint &ep)
{
return ep.address();
}
//
// socket::io
//
@ -691,17 +767,9 @@ noexcept
}
ircd::net::socket::scope_timeout::~scope_timeout()
noexcept try
noexcept
{
if(s)
s->timer.cancel();
}
catch(const std::exception &e)
{
log.error("socket(%p) ~scope_timeout: %s",
(const void *)s,
e.what());
return;
cancel();
}
bool
@ -713,7 +781,7 @@ noexcept try
auto *const s{this->s};
this->s = nullptr;
s->timer.cancel();
s->cancel_timeout();
return true;
}
catch(const std::exception &e)
@ -798,10 +866,6 @@ ircd::net::socket::socket(asio::ssl::context &ssl,
{
*ios
}
,timedout
{
false
}
{
}
@ -820,7 +884,7 @@ noexcept try
}
catch(const std::exception &e)
{
log.error("socket(%p): close: %s", this, e.what());
log.critical("socket(%p): close: %s", this, e.what());
return;
}
@ -905,9 +969,9 @@ ircd::net::socket::connect(const ip::tcp::endpoint &ep,
}
catch(const std::exception &e)
{
log.error("socket(%p): connect: unhandled exception from user callback: %s",
(const void *)this,
e.what());
log.critical("socket(%p): connect: unhandled exception from user callback: %s",
(const void *)this,
e.what());
}
}};
@ -934,8 +998,8 @@ ircd::net::socket::connect(const ip::tcp::endpoint &ep,
ssl.async_handshake(handshake, std::move(handshake_handler));
}};
sd.async_connect(ep, std::move(connect_handler));
set_timeout(timeout);
sd.async_connect(ep, std::move(connect_handler));
}
bool
@ -970,11 +1034,17 @@ try
sd.shutdown(ip::tcp::socket::shutdown_receive);
return true;
case dc::SSL_NOTIFY_YIELD:
case dc::SSL_NOTIFY_YIELD: if(likely(ctx::current))
{
const life_guard<socket> lg{*this};
const scope_timeout ts{*this, 8s};
ssl.async_shutdown(yield_context{to_asio{}});
error_code ec;
sd.close(ec);
if(ec)
log.error("socket(%p): close: %s: %s",
this,
string(ec));
return true;
}
@ -982,7 +1052,8 @@ try
{
set_timeout(8s);
ssl.async_shutdown([s(shared_from_this())]
(boost::system::error_code ec) noexcept
(error_code ec)
noexcept
{
if(!s->timedout)
s->cancel_timeout();
@ -990,8 +1061,7 @@ try
if(ec)
log.warning("socket(%p): SSL_NOTIFY: %s: %s",
s.get(),
ec.category().name(),
ec.message());
string(ec));
if(!s->sd.is_open())
return;
@ -1001,8 +1071,7 @@ try
if(ec)
log.warning("socket(%p): after SSL_NOTIFY: %s: %s",
s.get(),
ec.category().name(),
ec.message());
string(ec));
});
return true;
}
@ -1025,8 +1094,7 @@ catch(const boost::system::system_error &e)
if(ec)
log.warning("socket(%p): after disconnect: %s: %s",
this,
ec.category().name(),
ec.message());
string(ec));
throw;
}
@ -1063,12 +1131,6 @@ ircd::net::socket::operator()(handler h)
/// blocking any context and using any stack space whatsoever, i.e full
/// asynchronous mode.
///
/// boost::asio has no direct way to accomplish this because the buffer size
/// must be positive so we use a little trick to read a single byte with
/// MSG_PEEK as our indication. This is done directly on the socket and
/// not through the SSL cipher, but we don't want this byte anyway. This
/// isn't such a great trick.
///
void
ircd::net::socket::operator()(const milliseconds &timeout,
handler callback)
@ -1078,7 +1140,7 @@ ircd::net::socket::operator()(const milliseconds &timeout,
ip::tcp::socket::message_peek
};
static char buffer[1];
static char buffer[0];
static const asio::mutable_buffers_1 buffers
{
buffer, sizeof(buffer)
@ -1089,6 +1151,7 @@ ircd::net::socket::operator()(const milliseconds &timeout,
std::bind(&socket::handle, this, weak_from(*this), std::move(callback), ph::_1, ph::_2)
};
assert(connected());
set_timeout(timeout);
sd.async_receive(buffers, flags, std::move(handler));
}
@ -1101,13 +1164,17 @@ ircd::net::socket::handle(const std::weak_ptr<socket> wp,
noexcept try
{
const life_guard<socket> s{wp};
log.debug("socket(%p): %zu bytes: %s: %s",
this,
bytes,
string(ec));
// This handler and the timeout handler are responsible for canceling each other
// when one or the other is entered. If the timeout handler has already fired for
// a timeout on the socket, `timedout` will be `true` and this handler will be
// entered with an `operation_canceled` error.
if(!timedout)
timer.cancel();
cancel_timeout();
else
assert(ec == boost::system::errc::operation_canceled);
@ -1115,10 +1182,9 @@ noexcept try
// user's callback. Otherwise they are passed up.
if(!handle_error(ec))
{
log.warning("socket(%p): %s",
this,
ec.category().name(),
ec.message());
log.error("socket(%p): %s",
this,
string(ec));
return;
}
@ -1134,6 +1200,13 @@ catch(const std::bad_weak_ptr &e)
e.what());
assert(0);
}
catch(const boost::system::system_error &e)
{
log.error("socket(%p): handle: %s %s",
this,
string(ec));
assert(0);
}
catch(const std::exception &e)
{
log.error("socket(%p): handle: %s",
@ -1151,9 +1224,9 @@ noexcept try
}
catch(const std::exception &e)
{
log.error("socket(%p): async handler: unhandled exception: %s",
this,
e.what());
log.critical("socket(%p): async handler: unhandled exception: %s",
this,
e.what());
}
bool
@ -1165,10 +1238,9 @@ ircd::net::socket::handle_error(const error_code &ec)
using boost::asio::error::get_misc_category;
if(ec != success)
log.error("socket(%p): handle error: %s: %s",
this,
ec.category().name(),
ec.message());
log.warning("socket(%p): handle error: %s: %s",
this,
string(ec));
if(ec.category() == get_system_category()) switch(ec.value())
{
@ -1227,15 +1299,19 @@ noexcept try
case success:
{
sd.cancel();
assert(timedout == false);
timedout = true;
break;
}
// A cancelation means there was no timeout.
case operation_canceled:
{
assert(ec.category() == boost::system::get_system_category());
assert(timedout == false);
timedout = false;
break;
}
// All other errors are unexpected, logged and ignored here.
default:
@ -1278,6 +1354,7 @@ ircd::net::socket::cancel_timeout()
noexcept
{
boost::system::error_code ec;
timedout = false;
timer.cancel(ec);
return ec;
}
@ -1299,6 +1376,7 @@ const
void
ircd::net::socket::set_timeout(const milliseconds &t)
{
cancel_timeout();
if(t < milliseconds(0))
return;
@ -1310,6 +1388,7 @@ void
ircd::net::socket::set_timeout(const milliseconds &t,
handler h)
{
cancel_timeout();
if(t < milliseconds(0))
return;
@ -1317,6 +1396,81 @@ ircd::net::socket::set_timeout(const milliseconds &t,
timer.async_wait(std::move(h));
}
///////////////////////////////////////////////////////////////////////////////
//
// net/asio.h
//
std::string
ircd::net::string(const ip::address &addr)
{
return addr.to_string();
}
std::string
ircd::net::string(const ip::tcp::endpoint &ep)
{
std::string ret(256, char{});
const auto addr{string(net::addr(ep))};
const auto data{const_cast<char *>(ret.data())};
ret.resize(snprintf(data, ret.size(), "%s:%u", addr.c_str(), port(ep)));
return ret;
}
std::string
ircd::net::host(const ip::tcp::endpoint &ep)
{
return string(addr(ep));
}
boost::asio::ip::address
ircd::net::addr(const ip::tcp::endpoint &ep)
{
return ep.address();
}
uint16_t
ircd::net::port(const ip::tcp::endpoint &ep)
{
return ep.port();
}
std::string
ircd::net::string(const boost::system::system_error &e)
{
return string(e.code());
}
std::string
ircd::net::string(const boost::system::error_code &ec)
{
std::string ret(128, char{});
ret.resize(string(mutable_buffer{ret}, ec).size());
return ret;
}
ircd::string_view
ircd::net::string(const mutable_buffer &buf,
const boost::system::system_error &e)
{
return string(buf, e.code());
}
ircd::string_view
ircd::net::string(const mutable_buffer &buf,
const boost::system::error_code &ec)
{
const auto len
{
fmt::sprintf
{
buf, "%s: %s", ec.category().name(), ec.message()
}
};
return { data(buf), size_t(len) };
}
///////////////////////////////////////////////////////////////////////////////
//
// net/remote.h