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

ircd::net: Various fixes / error handling / api.

This commit is contained in:
Jason Volk 2017-10-26 15:36:31 -07:00
parent f36d3e2209
commit 2ce9b0521f
4 changed files with 299 additions and 140 deletions

View file

@ -42,6 +42,7 @@ namespace ircd::net
struct remote; struct remote;
struct socket; struct socket;
struct listener; struct listener;
enum class dc;
// SNOMASK 'N' "net" // SNOMASK 'N' "net"
extern struct log::log log; extern struct log::log log;
@ -50,6 +51,16 @@ namespace ircd::net
#include "remote.h" #include "remote.h"
#include "listener.h" #include "listener.h"
enum class ircd::net::dc
{
RST, ///< hardest immediate termination
FIN, ///< sd graceful shutdown both directions
FIN_SEND, ///< sd graceful shutdown send side
FIN_RECV, ///< sd graceful shutdown recv side
SSL_NOTIFY, ///< SSL close_notify (async, errors ignored)
SSL_NOTIFY_YIELD, ///< SSL close_notify (yields context, throws)
};
// Public interface to socket.h because it is not included here. // Public interface to socket.h because it is not included here.
namespace ircd::net namespace ircd::net
{ {
@ -69,6 +80,9 @@ namespace ircd::net
size_t read(socket &, const iov<mutable_buffer> &); // read_all size_t read(socket &, const iov<mutable_buffer> &); // read_all
size_t read(socket &, const mutable_buffer &); // read_all size_t read(socket &, const mutable_buffer &); // read_all
size_t read(socket &, iov<mutable_buffer> &); // read_some size_t read(socket &, iov<mutable_buffer> &); // read_some
std::shared_ptr<socket> connect(const remote &, const milliseconds &timeout = 30000ms);
bool disconnect(socket &, const dc &type = dc::SSL_NOTIFY) noexcept;
} }
namespace ircd namespace ircd

View file

@ -42,9 +42,9 @@ namespace ircd::net
uint16_t port(const ip::tcp::endpoint &); uint16_t port(const ip::tcp::endpoint &);
ip::address addr(const ip::tcp::endpoint &); ip::address addr(const ip::tcp::endpoint &);
std::string host(const ip::tcp::endpoint &); std::string host(const ip::tcp::endpoint &);
std::string string(const ip::address &); std::string string(const ip::address &);
std::string string(const ip::tcp::endpoint &); std::string string(const ip::tcp::endpoint &);
std::shared_ptr<socket> connect(const ip::tcp::endpoint &remote, const milliseconds &timeout);
} }
namespace ircd namespace ircd
@ -62,7 +62,6 @@ struct ircd::net::socket
struct io; struct io;
struct stat; struct stat;
struct scope_timeout; struct scope_timeout;
enum class dc;
struct stat struct stat
{ {
@ -127,21 +126,8 @@ struct ircd::net::socket
void connect(const ip::tcp::endpoint &ep, const milliseconds &timeout, handler callback); void connect(const ip::tcp::endpoint &ep, const milliseconds &timeout, handler callback);
void connect(const ip::tcp::endpoint &ep, const milliseconds &timeout = 30000ms); void connect(const ip::tcp::endpoint &ep, const milliseconds &timeout = 30000ms);
void connect(const net::remote &, const milliseconds &timeout = 30000ms); void connect(const net::remote &, const milliseconds &timeout = 30000ms);
void disconnect(const dc &type); bool disconnect(const dc &type);
// Construct, resolve and connect client socket to remote host (yields)
socket(const net::remote &,
const milliseconds &timeout = 30000ms,
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 = 30000ms,
asio::ssl::context &ssl = sslv23_client,
boost::asio::io_service *const &ios = ircd::ios);
// Construct socket only
socket(asio::ssl::context &ssl = sslv23_client, socket(asio::ssl::context &ssl = sslv23_client,
boost::asio::io_service *const &ios = ircd::ios); boost::asio::io_service *const &ios = ircd::ios);
@ -182,16 +168,6 @@ class ircd::net::socket::io
io(struct socket &, struct stat &, const std::function<size_t ()> &closure); 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> template<class iov>
auto auto
ircd::net::socket::write(const iov &bufs) ircd::net::socket::write(const iov &bufs)

View file

@ -54,15 +54,11 @@ ctx::pool request
// Container for all active clients (connections) for iteration purposes. // Container for all active clients (connections) for iteration purposes.
client::list client::clients; client::list client::clients;
bool handle_ec_timeout(client &); static bool handle_ec(client &, const net::error_code &);
bool handle_ec_eof(client &);
bool handle_ec_success(client &);
bool handle_ec(client &, const net::error_code &);
void async_recv_next(std::shared_ptr<client>, const milliseconds &timeout); void async_recv_next(std::shared_ptr<client>, const milliseconds &timeout);
void async_recv_next(std::shared_ptr<client>); void async_recv_next(std::shared_ptr<client>);
void disconnect(client &, const socket::dc & = socket::dc::RST); void disconnect(client &, const net::dc & = net::dc::RST);
void disconnect_all(); void disconnect_all();
template<class... args> std::shared_ptr<client> make_client(args&&...); template<class... args> std::shared_ptr<client> make_client(args&&...);
@ -196,7 +192,7 @@ ircd::client::client(const hostport &host_port,
const seconds &timeout) const seconds &timeout)
:client :client
{ {
std::make_shared<socket>(host_port, timeout) net::connect(host_port, timeout)
} }
{ {
} }
@ -211,8 +207,7 @@ ircd::client::client(std::shared_ptr<socket> sock)
ircd::client::~client() ircd::client::~client()
noexcept try noexcept try
{ {
if(sock) disconnect(*this, net::dc::SSL_NOTIFY);
sock->disconnect(socket::dc::SSL_NOTIFY);
} }
catch(const std::exception &e) catch(const std::exception &e)
{ {
@ -232,7 +227,7 @@ noexcept try
{ {
const auto header_max{8192}; const auto header_max{8192};
const auto content_max{65536}; const auto content_max{65536};
unique_buffer<mutable_buffer> buffer const unique_buffer<mutable_buffer> buffer
{ {
header_max + content_max header_max + content_max
}; };
@ -251,18 +246,21 @@ noexcept try
} }
catch(const boost::system::system_error &e) catch(const boost::system::system_error &e)
{ {
using boost::asio::error::eof;
using boost::asio::error::broken_pipe;
using boost::asio::error::connection_reset;
using namespace boost::system::errc; using namespace boost::system::errc;
using boost::system::get_system_category;
using boost::asio::error::get_ssl_category;
using boost::asio::error::get_misc_category;
switch(e.code().value()) const auto ec
{
e.code()
};
if(ec.category() == get_system_category()) switch(ec.value())
{ {
case success: case success:
assert(0);
return true; return true;
case eof:
case broken_pipe: case broken_pipe:
case connection_reset: case connection_reset:
case not_connected: case not_connected:
@ -272,8 +270,27 @@ catch(const boost::system::system_error &e)
default: default:
break; break;
} }
else if(ec.category() == get_misc_category()) switch(ec.value())
{
case boost::asio::error::eof:
return false;
default:
break;
}
else if(ec.category() == get_ssl_category()) switch(ec.value())
{
case SSL_R_SHORT_READ:
return false;
default:
break;
}
log::critical("client(%p): (unexpected) system_error: %s",
(const void *)this,
e.what());
log::critical("(unexpected) system_error: %s", e.what());
if(ircd::debugmode) if(ircd::debugmode)
throw; throw;
@ -297,11 +314,11 @@ ircd::handle_request(client &client,
try try
{ {
client.request_timer = ircd::timer{}; client.request_timer = ircd::timer{};
client.sock->set_timeout(request_timeout, [&client] client.sock->set_timeout(request_timeout, [client(shared_from(client))]
(const net::error_code &ec) (const net::error_code &ec)
{ {
if(!ec) if(!ec)
client.sock->cancel(); disconnect(*client, net::dc::SSL_NOTIFY_YIELD);
}); });
bool ret{true}; bool ret{true};
@ -328,10 +345,13 @@ catch(const http::error &e)
switch(e.code) switch(e.code)
{ {
case http::BAD_REQUEST: return false; case http::BAD_REQUEST:
case http::INTERNAL_SERVER_ERROR: return false; case http::REQUEST_TIMEOUT:
case http::REQUEST_TIMEOUT: return false; case http::INTERNAL_SERVER_ERROR:
default: return true; return false;
default:
return true;
} }
} }
@ -382,7 +402,7 @@ ircd::disconnect_all()
{ {
for(auto &client : client::clients) try for(auto &client : client::clients) try
{ {
disconnect(*client, socket::dc::RST); disconnect(*client, net::dc::RST);
} }
catch(const std::exception &e) catch(const std::exception &e)
{ {
@ -392,10 +412,10 @@ ircd::disconnect_all()
void void
ircd::disconnect(client &client, ircd::disconnect(client &client,
const socket::dc &type) const net::dc &type)
{ {
auto &sock(*client.sock); if(likely(client.sock))
sock.disconnect(type); disconnect(*client.sock, type);
} }
void void
@ -439,46 +459,85 @@ ircd::async_recv_next(std::shared_ptr<client> client,
// of the ircd::context system. The context the closure ends up getting is the next // 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 // available from the request pool, which may not be available immediately so this
// handler might be queued for some time after this call returns. // handler might be queued for some time after this call returns.
request([client(std::move(client)), timeout] request([ec, client, timeout]
{ {
// Right here this handler is executing on an ircd::context with its own // Right here this handler is executing on an ircd::context with its own
// stack dedicated to the lifetime of this request. If client::main() // stack dedicated to the lifetime of this request. If client::main()
// returns true, we bring the client back into async mode to wait for // returns true, we bring the client back into async mode to wait for
// the next request. Otherwise, unless the client was preserved by // the next request.
// functionality in main(), it will go out of scope after this which
// will disconnect the socket and destroy the client and return this
// context to the request pool.
if(client->main()) if(client->main())
async_recv_next(client, timeout); async_recv_next(client, timeout);
}); });
}); });
} }
namespace ircd
{
static bool handle_ec_success(client &);
static bool handle_ec_timeout(client &);
static bool handle_ec_eof(client &);
static bool handle_ec_short_read(client &);
static bool handle_ec_default(client &, const net::error_code &);
}
bool bool
ircd::handle_ec(client &client, ircd::handle_ec(client &client,
const net::error_code &ec) const net::error_code &ec)
{ {
using namespace boost::system::errc; using namespace boost::system::errc;
using boost::asio::error::eof; using boost::system::get_system_category;
using boost::asio::error::get_ssl_category;
using boost::asio::error::get_misc_category;
switch(ec.value()) if(ec.category() == get_system_category()) switch(ec.value())
{ {
case success: return handle_ec_success(client); case success: return handle_ec_success(client);
case eof: return handle_ec_eof(client);
case operation_canceled: return handle_ec_timeout(client); case operation_canceled: return handle_ec_timeout(client);
default: default: return handle_ec_default(client, ec);
{
log::debug("client(%p): %s", &client, ec.message());
disconnect(client, socket::dc::RST);
return false;
}
} }
else if(ec.category() == get_misc_category()) switch(ec.value())
{
case asio::error::eof: return handle_ec_eof(client);
default: return handle_ec_default(client, ec);
}
else if(ec.category() == get_ssl_category()) switch(ec.value())
{
case SSL_R_SHORT_READ: return handle_ec_short_read(client);
default: return handle_ec_default(client, ec);
}
else return handle_ec_default(client, ec);
} }
bool bool
ircd::handle_ec_success(client &client) ircd::handle_ec_default(client &client,
const net::error_code &ec)
{ {
return true; log::debug("client(%p): %s: %s",
&client,
ec.category().name(),
ec.message());
disconnect(client, net::dc::SSL_NOTIFY);
return false;
}
bool
ircd::handle_ec_short_read(client &client)
try
{
log::debug("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());
return false;
} }
bool bool
@ -488,7 +547,7 @@ try
log::debug("client[%s]: EOF", log::debug("client[%s]: EOF",
string(remote(client))); string(remote(client)));
disconnect(client, socket::dc::RST); disconnect(client, net::dc::RST);
return false; return false;
} }
catch(const std::exception &e) catch(const std::exception &e)
@ -508,7 +567,7 @@ try
log::debug("client[%s]: disconnecting after inactivity timeout", log::debug("client[%s]: disconnecting after inactivity timeout",
string(remote(client))); string(remote(client)));
disconnect(client, socket::dc::SSL_NOTIFY); disconnect(client, net::dc::SSL_NOTIFY);
return false; return false;
} }
catch(const std::exception &e) catch(const std::exception &e)
@ -519,3 +578,9 @@ catch(const std::exception &e)
return false; return false;
} }
bool
ircd::handle_ec_success(client &client)
{
return true;
}

View file

@ -293,17 +293,18 @@ noexcept try
this->next(); this->next();
}}; }};
ip::tcp::socket &sd(*sock);
log.debug("%s: socket(%p) accepted %s", log.debug("%s: socket(%p) accepted %s",
std::string(*this), std::string(*this),
sock.get(), sock.get(),
string(sock->remote())); string(sock->remote()));
//ip::tcp::socket &sd(*sock);
//static const asio::socket_base::keep_alive keep_alive(true); //static const asio::socket_base::keep_alive keep_alive(true);
//sd.set_option(keep_alive); //sd.set_option(keep_alive);
static const asio::socket_base::linger linger{true, 10}; //static const asio::socket_base::linger linger{true, 10};
sd.set_option(linger); //sd.set_option(linger);
//sd.non_blocking(false); //sd.non_blocking(false);
@ -330,7 +331,9 @@ catch(const std::exception &e)
bool bool
ircd::net::listener::acceptor::accept_error(const error_code &ec) ircd::net::listener::acceptor::accept_error(const error_code &ec)
{ {
switch(ec.value()) using boost::system::get_system_category;
if(ec.category() == get_system_category()) switch(ec.value())
{ {
using namespace boost::system::errc; using namespace boost::system::errc;
@ -341,8 +344,10 @@ ircd::net::listener::acceptor::accept_error(const error_code &ec)
return true; return true;
default: default:
throw boost::system::system_error(ec); break;
} }
throw boost::system::system_error(ec);
} }
void void
@ -372,7 +377,9 @@ catch(const std::exception &e)
bool bool
ircd::net::listener::acceptor::handshake_error(const error_code &ec) ircd::net::listener::acceptor::handshake_error(const error_code &ec)
{ {
switch(ec.value()) using boost::system::get_system_category;
if(ec.category() == get_system_category()) switch(ec.value())
{ {
using namespace boost::system::errc; using namespace boost::system::errc;
@ -383,8 +390,10 @@ ircd::net::listener::acceptor::handshake_error(const error_code &ec)
return true; return true;
default: default:
throw boost::system::system_error(ec); break;
} }
throw boost::system::system_error(ec);
} }
void void
@ -724,40 +733,57 @@ ircd::net::socket::scope_timeout::release()
return s != nullptr; return s != nullptr;
} }
std::shared_ptr<ircd::net::socket>
ircd::net::connect(const net::remote &remote,
const milliseconds &timeout)
{
const asio::ip::tcp::endpoint ep
{
is_v6(remote)? asio::ip::tcp::endpoint
{
asio::ip::address_v6 { std::get<remote.IP>(remote) }, port(remote)
}
: asio::ip::tcp::endpoint
{
asio::ip::address_v4 { host4(remote) }, port(remote)
},
};
return connect(ep, timeout);
}
std::shared_ptr<ircd::net::socket>
ircd::net::connect(const ip::tcp::endpoint &remote,
const milliseconds &timeout)
{
const auto ret(std::make_shared<socket>());
ret->connect(remote, timeout);
return ret;
}
bool
ircd::net::disconnect(socket &socket,
const dc &type)
noexcept try
{
socket.disconnect(type);
return true;
}
catch(const std::exception &e)
{
/*
log::error("socket(%p): disconnect: type: %d: %s",
this,
int(type),
e.what());
*/
return false;
}
// //
// socket // socket
// //
ircd::net::socket::socket(const net::remote &remote,
const milliseconds &timeout,
asio::ssl::context &ssl,
boost::asio::io_service *const &ios)
:socket
{
is_v6(remote)? asio::ip::tcp::endpoint
{
asio::ip::address_v6 { std::get<remote.IP>(remote) }, port(remote)
}
: asio::ip::tcp::endpoint
{
asio::ip::address_v4 { host4(remote) }, port(remote)
},
timeout,
ssl,
ios
}
{
}
ircd::net::socket::socket(const ip::tcp::endpoint &remote,
const milliseconds &timeout,
asio::ssl::context &ssl,
boost::asio::io_service *const &ios)
:socket{ssl, ios}
{
connect(remote, timeout);
}
ircd::net::socket::socket(asio::ssl::context &ssl, ircd::net::socket::socket(asio::ssl::context &ssl,
boost::asio::io_service *const &ios) boost::asio::io_service *const &ios)
:sd :sd
@ -779,10 +805,18 @@ ircd::net::socket::socket(asio::ssl::context &ssl,
{ {
} }
/// The dtor asserts that the socket is not open/connected requiring a
/// an SSL close_notify. There's no more room for async callbacks via
/// shared_ptr after this dtor.
ircd::net::socket::~socket() ircd::net::socket::~socket()
noexcept try noexcept try
{ {
//disconnect(dc::RST); if(unlikely(RB_DEBUG_LEVEL && connected()))
log.critical("Failed to ensure socket(%p) is disconnected from %s before dtor.",
this,
string(remote()));
assert(!connected());
} }
catch(const std::exception &e) catch(const std::exception &e)
{ {
@ -797,13 +831,13 @@ ircd::net::socket::connect(const ip::tcp::endpoint &ep,
const milliseconds &timeout) const milliseconds &timeout)
try try
{ {
const life_guard<socket> lg{*this};
const scope_timeout ts{*this, timeout};
log.debug("socket(%p) attempting connect to remote: %s for the next %ld$ms", log.debug("socket(%p) attempting connect to remote: %s for the next %ld$ms",
this, this,
string(ep), string(ep),
timeout.count()); timeout.count());
ip::tcp::socket &sd(*this);
const scope_timeout ts{*this, timeout};
sd.async_connect(ep, yield_context{to_asio{}}); sd.async_connect(ep, yield_context{to_asio{}});
log.debug("socket(%p) connected to remote: %s from local: %s; performing handshake...", log.debug("socket(%p) connected to remote: %s from local: %s; performing handshake...",
this, this,
@ -822,6 +856,9 @@ catch(const std::exception &e)
this, this,
string(ep), string(ep),
e.what()); e.what());
disconnect(dc::RST);
throw;
} }
/// Attempt to connect and ssl handshake remote; yields ircd::ctx; throws timeout /// Attempt to connect and ssl handshake remote; yields ircd::ctx; throws timeout
@ -856,18 +893,37 @@ ircd::net::socket::connect(const ip::tcp::endpoint &ep,
(const error_code &ec) (const error_code &ec)
noexcept noexcept
{ {
if(!timedout) if(timedout)
cancel_timeout();
else
assert(ec == boost::system::errc::operation_canceled); assert(ec == boost::system::errc::operation_canceled);
callback(ec); if(!timedout)
cancel_timeout();
try
{
callback(ec);
}
catch(const std::exception &e)
{
log.error("socket(%p): connect: unhandled exception from user callback: %s",
(const void *)this,
e.what());
}
}}; }};
auto connect_handler{[this, handshake_handler(std::move(handshake_handler))] auto connect_handler{[this, handshake_handler(std::move(handshake_handler))]
(const error_code &ec) (const error_code &ec)
noexcept noexcept
{ {
// Even though the branch on ec below should cancel the timeout on
// error, the timeout still needs to be canceled if else anything bad
// happens in the remainder of this frame too.
const unwind::exceptional cancels{[this]
{
cancel_timeout();
}};
// A connect error
if(ec) if(ec)
{ {
handshake_handler(ec); handshake_handler(ec);
@ -878,12 +934,11 @@ ircd::net::socket::connect(const ip::tcp::endpoint &ep,
ssl.async_handshake(handshake, std::move(handshake_handler)); ssl.async_handshake(handshake, std::move(handshake_handler));
}}; }};
set_timeout(timeout);
ip::tcp::socket &sd(*this);
sd.async_connect(ep, std::move(connect_handler)); sd.async_connect(ep, std::move(connect_handler));
set_timeout(timeout);
} }
void bool
ircd::net::socket::disconnect(const dc &type) ircd::net::socket::disconnect(const dc &type)
try try
{ {
@ -901,51 +956,58 @@ try
default: default:
case dc::RST: case dc::RST:
sd.close(); sd.close();
break; return true;
case dc::FIN: case dc::FIN:
sd.shutdown(ip::tcp::socket::shutdown_both); sd.shutdown(ip::tcp::socket::shutdown_both);
break; return true;
case dc::FIN_SEND: case dc::FIN_SEND:
sd.shutdown(ip::tcp::socket::shutdown_send); sd.shutdown(ip::tcp::socket::shutdown_send);
break; return true;
case dc::FIN_RECV: case dc::FIN_RECV:
sd.shutdown(ip::tcp::socket::shutdown_receive); sd.shutdown(ip::tcp::socket::shutdown_receive);
break; return true;
case dc::SSL_NOTIFY_YIELD: case dc::SSL_NOTIFY_YIELD:
{ {
const life_guard<socket> lg{*this};
const scope_timeout ts{*this, 8s};
ssl.async_shutdown(yield_context{to_asio{}}); ssl.async_shutdown(yield_context{to_asio{}});
sd.close(); return true;
break;
} }
case dc::SSL_NOTIFY: case dc::SSL_NOTIFY:
{ {
set_timeout(8s);
ssl.async_shutdown([s(shared_from_this())] ssl.async_shutdown([s(shared_from_this())]
(boost::system::error_code ec) noexcept (boost::system::error_code ec) noexcept
{ {
if(!s->timedout)
s->cancel_timeout();
if(ec) if(ec)
{ log.warning("socket(%p): SSL_NOTIFY: %s: %s",
log.warning("socket(%p): close_notify: %s",
s.get(), s.get(),
ec.category().name(),
ec.message()); ec.message());
return;
}
if(s->sd.is_open()) if(!s->sd.is_open())
s->sd.close(ec); return;
s->sd.close(ec);
if(ec) if(ec)
log.warning("socket(%p): close(): %s", log.warning("socket(%p): after SSL_NOTIFY: %s: %s",
s.get(), s.get(),
ec.category().name(),
ec.message()); ec.message());
}); });
break; return true;
} }
} }
else return false;
} }
catch(const boost::system::system_error &e) catch(const boost::system::system_error &e)
{ {
@ -953,6 +1015,18 @@ catch(const boost::system::system_error &e)
(const void *)this, (const void *)this,
uint(type), uint(type),
e.what()); e.what());
if(!sd.is_open())
throw;
boost::system::error_code ec;
sd.close(ec);
if(ec)
log.warning("socket(%p): after disconnect: %s: %s",
this,
ec.category().name(),
ec.message());
throw; throw;
} }
@ -1041,7 +1115,10 @@ noexcept try
// user's callback. Otherwise they are passed up. // user's callback. Otherwise they are passed up.
if(!handle_error(ec)) if(!handle_error(ec))
{ {
log.debug("socket(%p): %s", this, ec.message()); log.warning("socket(%p): %s",
this,
ec.category().name(),
ec.message());
return; return;
} }
@ -1083,8 +1160,17 @@ bool
ircd::net::socket::handle_error(const error_code &ec) ircd::net::socket::handle_error(const error_code &ec)
{ {
using namespace boost::system::errc; using namespace boost::system::errc;
using boost::system::get_system_category;
using boost::asio::error::get_ssl_category;
using boost::asio::error::get_misc_category;
switch(ec.value()) if(ec != success)
log.error("socket(%p): handle error: %s: %s",
this,
ec.category().name(),
ec.message());
if(ec.category() == get_system_category()) switch(ec.value())
{ {
// A success is not an error; can call the user handler // A success is not an error; can call the user handler
case success: case success:
@ -1096,11 +1182,6 @@ ircd::net::socket::handle_error(const error_code &ec)
case operation_canceled: case operation_canceled:
return timedout; return timedout;
// This indicates the remote closed the socket, we still
// pass this up to the user so they can handle it.
case boost::asio::error::eof:
return true;
// This is a condition which we hide from the user. // This is a condition which we hide from the user.
case bad_file_descriptor: case bad_file_descriptor:
return false; return false;
@ -1109,6 +1190,28 @@ ircd::net::socket::handle_error(const error_code &ec)
default: default:
return true; return true;
} }
else if(ec.category() == get_misc_category()) switch(ec.value())
{
// This indicates the remote closed the socket, we still
// pass this up to the user so they can know that too.
case boost::asio::error::eof:
return true;
default:
return true;
}
else if(ec.category() == get_ssl_category()) switch(ec.value())
{
// Docs say this means we read less bytes off the socket than desired.
case SSL_R_SHORT_READ:
return true;
default:
return true;
}
assert(0);
return true;
} }
void void
@ -1130,6 +1233,7 @@ noexcept try
// A cancelation means there was no timeout. // A cancelation means there was no timeout.
case operation_canceled: case operation_canceled:
assert(ec.category() == boost::system::get_system_category());
timedout = false; timedout = false;
break; break;
@ -1142,7 +1246,7 @@ catch(const boost::system::system_error &e)
{ {
log.error("socket(%p): handle_timeout: unexpected: %s\n", log.error("socket(%p): handle_timeout: unexpected: %s\n",
(const void *)this, (const void *)this,
e.what()); e.what());
} }
catch(const std::exception &e) catch(const std::exception &e)
{ {