mirror of
https://github.com/matrix-construct/construct
synced 2024-11-15 14:31:11 +01:00
ircd::net: Various net/client/listener bugfixes/cleanup.
This commit is contained in:
parent
20961bffa6
commit
2930b93dcb
8 changed files with 423 additions and 193 deletions
|
@ -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
|
||||
|
|
|
@ -71,6 +71,8 @@ struct ircd::client
|
|||
|
||||
struct ircd::client::init
|
||||
{
|
||||
void interrupt();
|
||||
|
||||
init();
|
||||
~init() noexcept;
|
||||
};
|
||||
|
|
56
include/ircd/net/asio.h
Normal file
56
include/ircd/net/asio.h
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
log::error("client(%p): (unexpected) %s: (%d) %s",
|
||||
(const void *)this,
|
||||
e.what());
|
||||
|
||||
if(ircd::debugmode)
|
||||
throw;
|
||||
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;
|
||||
|
||||
#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 socket::scope_timeout timeout
|
||||
{
|
||||
*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);
|
||||
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());
|
||||
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,7 +548,7 @@ bool
|
|||
ircd::handle_ec_short_read(client &client)
|
||||
try
|
||||
{
|
||||
log::debug("client[%s]: short_read",
|
||||
log::warning("client[%s]: short_read",
|
||||
string(remote(client)));
|
||||
|
||||
disconnect(client, net::dc::RST);
|
||||
|
@ -533,7 +556,7 @@ try
|
|||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::warning("client(%p): short_read: %s",
|
||||
log::error("client(%p): short_read: %s",
|
||||
&client,
|
||||
e.what());
|
||||
|
||||
|
@ -552,7 +575,7 @@ try
|
|||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::warning("client(%p): EOF: %s",
|
||||
log::error("client(%p): EOF: %s",
|
||||
&client,
|
||||
e.what());
|
||||
|
||||
|
@ -564,7 +587,7 @@ ircd::handle_ec_timeout(client &client)
|
|||
try
|
||||
{
|
||||
assert(bool(client.sock));
|
||||
log::debug("client[%s]: disconnecting after inactivity timeout",
|
||||
log::warning("client[%s]: disconnecting after inactivity timeout",
|
||||
string(remote(client)));
|
||||
|
||||
disconnect(client, net::dc::SSL_NOTIFY);
|
||||
|
@ -572,7 +595,7 @@ try
|
|||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log::warning("client(%p): timeout: %s",
|
||||
log::error("client(%p): timeout: %s",
|
||||
&client,
|
||||
e.what());
|
||||
|
||||
|
|
12
ircd/ircd.cc
12
ircd/ircd.cc
|
@ -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)
|
||||
{
|
||||
|
|
412
ircd/net.cc
412
ircd/net.cc
|
@ -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,7 +969,7 @@ 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",
|
||||
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",
|
||||
log.error("socket(%p): %s",
|
||||
this,
|
||||
ec.category().name(),
|
||||
ec.message());
|
||||
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,7 +1224,7 @@ noexcept try
|
|||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log.error("socket(%p): async handler: unhandled exception: %s",
|
||||
log.critical("socket(%p): async handler: unhandled exception: %s",
|
||||
this,
|
||||
e.what());
|
||||
}
|
||||
|
@ -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",
|
||||
log.warning("socket(%p): handle error: %s: %s",
|
||||
this,
|
||||
ec.category().name(),
|
||||
ec.message());
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue