Mercurial > irccd
changeset 670:95ac3ace1610
Common: introduce new io code
To avoid code duplication in accept, connect, reading and writing we add a new
set of classes in `io` namespaces located in the following files:
- stream.hpp, acceptor.hpp, connector.hpp
These classes consist of pure abstract interfaces for I/O. Then we reimplement
them in the following files:
- socket_stream.hpp, socket_acceptor.hpp, socket_connector.hpp,
- tls_stream.hpp, tls_acceptor.hpp, tls_conncetor.hpp (for SSL).
This allows future independant connections such as DBus, fifo or any other fancy
optional stuff.
We also no longer need large class hierarchy such as `connection` for irccdctl
controller or transport_server, transport_client classes.
line wrap: on
line diff
--- a/cmake/function/IrccdDefineTest.cmake Fri Apr 06 22:06:07 2018 +0200 +++ b/cmake/function/IrccdDefineTest.cmake Tue Apr 10 21:20:30 2018 +0200 @@ -76,6 +76,8 @@ PRIVATE ${TEST_FLAGS} BOOST_TEST_DYN_LINK + TESTS_SOURCE_DIR="${tests_SOURCE_DIR}" + TESTS_BINARY_DIR="${tests_SOURCE_DIR}" CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}" CMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}" CMAKE_CURRENT_BINARY_DIR="${CMAKE_CURRENT_BINARY_DIR}"
--- a/irccdctl/cli.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/irccdctl/cli.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -16,8 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <boost/system/system_error.hpp> - #include <irccd/json_util.hpp> #include <irccd/options.hpp> #include <irccd/string_util.hpp> @@ -32,9 +30,9 @@ void cli::recv_response(ctl::controller& ctl, nlohmann::json req, handler_t handler) { - ctl.recv([&ctl, req, handler, this] (auto code, auto message) { + ctl.read([&ctl, req, handler, this] (auto code, auto message) { if (code) - throw boost::system::system_error(code); + throw std::system_error(code); const auto c = json_util::parser(message).get<std::string>("command"); @@ -50,9 +48,9 @@ void cli::request(ctl::controller& ctl, nlohmann::json req, handler_t handler) { - ctl.send(req, [&ctl, req, handler, this] (auto code) { + ctl.write(req, [&ctl, req, handler, this] (auto code) { if (code) - throw boost::system::system_error(code); + throw std::system_error(code); recv_response(ctl, std::move(req), std::move(handler)); });
--- a/irccdctl/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/irccdctl/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -26,15 +26,17 @@ #include <irccd/config.hpp> #include <irccd/json_util.hpp> #include <irccd/options.hpp> +#include <irccd/socket_connector.hpp> #include <irccd/string_util.hpp> #include <irccd/system.hpp> -#include <irccd/ctl/controller.hpp> -#include <irccd/ctl/ip_connection.hpp> +#if defined(HAVE_SSL) +# include <irccd/tls_connector.hpp> +#endif -#if !defined(IRCCD_SYSTEM_WINDOWS) -# include <irccd/ctl/local_connection.hpp> -#endif +#include <irccd/daemon/transport_server.hpp> + +#include <irccd/ctl/controller.hpp> #include "plugin_config_cli.hpp" #include "plugin_info_cli.hpp" @@ -68,6 +70,14 @@ #include "alias.hpp" #include "cli.hpp" +using boost::asio::ip::tcp; + +#if !defined(IRCCD_SYSTEM_WINDOWS) + +using boost::asio::local::stream_protocol; + +#endif + namespace irccd { namespace ctl { @@ -77,18 +87,10 @@ // Main service; boost::asio::io_service service; -#if defined(HAVE_SSL) - -// For tls_connection. -boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); - -#endif - // Global options. bool verbose = false; // Connection to instance. -std::unique_ptr<connection> conn; std::unique_ptr<controller> ctl; // List of all commands and alias. @@ -106,6 +108,25 @@ } /* + * resolve + * ------------------------------------------------------------------ + * + * Block unless host/port has been resolved. + */ +std::vector<tcp::endpoint> resolve(const std::string& host, const std::string& name) +{ + std::vector<tcp::endpoint> endpoints; + + tcp::resolver resolver(service); + tcp::resolver::query query(host, name); + + for (auto it = resolver.resolve(query); it != tcp::resolver::iterator(); ++it) + endpoints.push_back(*it); + + return endpoints; +} + +/* * read_connect_ip * ------------------------------------------------------------------- * @@ -118,35 +139,30 @@ * domain = "ipv4 or ipv6" (Optional, default: ipv4) * ssl = true | false */ -std::unique_ptr<connection> read_connect_ip(const ini::section& sc) +std::unique_ptr<io::connector> read_connect_ip(const ini::section& sc) { - std::unique_ptr<connection> conn; - std::string host; - ini::section::const_iterator it; - - if ((it = sc.find("host")) == sc.end()) - throw std::invalid_argument("missing host parameter"); - - host = it->value(); + const auto host = sc.get("host").value(); + const auto port = sc.get("port").value(); - if ((it = sc.find("port")) == sc.end()) - throw std::invalid_argument("missing port parameter"); + if (host.empty()) + throw transport_error(transport_error::invalid_hostname); + if (port.empty()) + throw transport_error(transport_error::invalid_port); - const auto port = string_util::to_uint<std::uint16_t>(it->value()); + const auto endpoints = resolve(host, port); - if (!port) - throw std::invalid_argument("invalid port parameter"); + if (string_util::is_boolean(sc.get("ssl").value())) { +#if defined(HAVE_SSL) + // TODO: support more parameters. + boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); - if ((it = sc.find("ssl")) != sc.end() && string_util::is_boolean(it->value())) -#if defined(HAVE_SSL) - conn = std::make_unique<tls_connection>(service, ctx, host, *port); + return std::make_unique<io::tls_connector<>>(std::move(ctx), service, endpoints); #else throw std::runtime_error("SSL disabled"); #endif - else - conn = std::make_unique<ip_connection>(service, host, *port); + } - return conn; + return std::make_unique<io::ip_connector>(service, endpoints); } /* @@ -159,15 +175,17 @@ * type = "unix" * path = "path to socket file" */ -std::unique_ptr<connection> read_connect_local(const ini::section& sc) +std::unique_ptr<io::connector> read_connect_local(const ini::section& sc) { #if !defined(IRCCD_SYSTEM_WINDOWS) - auto it = sc.find("path"); + using boost::asio::local::stream_protocol; + + const auto it = sc.find("path"); if (it == sc.end()) throw std::invalid_argument("missing path parameter"); - return std::make_unique<local_connection>(service, it->value()); + return std::make_unique<io::local_connector>(service, it->value()); #else (void)sc; @@ -183,20 +201,22 @@ */ void read_connect(const ini::section& sc) { - auto it = sc.find("type"); + const auto it = sc.find("type"); if (it == sc.end()) throw std::invalid_argument("missing type parameter"); + std::unique_ptr<io::connector> connector; + if (it->value() == "ip") - conn = read_connect_ip(sc); + connector = read_connect_ip(sc); else if (it->value() == "unix") - conn = read_connect_local(sc); + connector = read_connect_local(sc); else throw std::invalid_argument(string_util::sprintf("invalid type given: %s", it->value())); - if (conn) { - ctl = std::make_unique<controller>(*conn); + if (connector) { + ctl = std::make_unique<controller>(std::move(connector)); auto password = sc.find("password"); @@ -216,7 +236,7 @@ */ void read_general(const ini::section& sc) { - auto value = sc.find("verbose"); + const auto value = sc.find("verbose"); if (value != sc.end()) verbose = string_util::is_boolean(value->value()); @@ -293,26 +313,23 @@ * -h host or ip * -p port */ -std::unique_ptr<connection> parse_connect_ip(const option::result& options) +std::unique_ptr<io::connector> parse_connect_ip(const option::result& options) { option::result::const_iterator it; // Host (-h or --host). if ((it = options.find("-h")) == options.end() && (it = options.find("--host")) == options.end()) - throw std::invalid_argument("missing host argument (-h or --host)"); + throw transport_error(transport_error::invalid_hostname); - auto host = it->second; + const auto host = it->second; // Port (-p or --port). if ((it = options.find("-p")) == options.end() && (it = options.find("--port")) == options.end()) - throw std::invalid_argument("missing port argument (-p or --port)"); - - const auto port = string_util::to_uint<std::uint16_t>(it->second); + throw transport_error(transport_error::invalid_port); - if (!port) - throw std::invalid_argument("invalid port argument"); + const auto port = it->second; - return std::make_unique<ip_connection>(service, host, *port); + return std::make_unique<io::ip_connector>(service, resolve(host, port)); } /* @@ -323,7 +340,7 @@ * * -P file */ -std::unique_ptr<connection> parse_connect_local(const option::result& options) +std::unique_ptr<io::connector> parse_connect_local(const option::result& options) { #if !defined(IRCCD_SYSTEM_WINDOWS) option::result::const_iterator it; @@ -331,7 +348,7 @@ if ((it = options.find("-P")) == options.end() && (it = options.find("--path")) == options.end()) throw std::invalid_argument("missing path parameter (-P or --path)"); - return std::make_unique<local_connection>(service, it->second); + return std::make_unique<io::local_connector>(service, it->second); #else (void)options; @@ -354,15 +371,17 @@ if (it == options.end()) it = options.find("--type"); + std::unique_ptr<io::connector> connector; + if (it->second == "ip" || it->second == "ipv6") - conn = parse_connect_ip(options); + connector = parse_connect_ip(options); if (it->second == "unix") - conn = parse_connect_local(options); + connector = parse_connect_local(options); else throw std::invalid_argument(string_util::sprintf("invalid type given: %s", it->second)); - if (conn) - ctl = std::make_unique<controller>(*conn); + if (connector) + ctl = std::make_unique<controller>(std::move(connector)); } option::result parse(int& argc, char**& argv) @@ -507,11 +526,9 @@ void do_connect() { - bool connected = false; - ctl->connect([&] (auto code, auto info) { if (code) - throw boost::system::system_error(code); + throw std::system_error(code); if (verbose) { const json_util::parser parser(info); @@ -525,13 +542,9 @@ std::cout << string_util::sprintf("connected to irccd %d.%d.%d\n", *major, *minor, *patch); } - - connected = true; }); - while (!connected) - service.run(); - + service.run(); service.reset(); } @@ -543,9 +556,7 @@ args.push_back(argv[i]); exec(args); - - while (ctl->get_conn().is_active()) - service.run(); + service.run(); } } // !namespace @@ -599,7 +610,7 @@ try { irccd::ctl::do_connect(); irccd::ctl::do_exec(argc, argv); - } catch (const boost::system::system_error& ex) { + } catch (const std::system_error& ex) { std::cerr << "abort: " << ex.code().message() << std::endl; } catch (const std::exception& ex) { std::cerr << "abort: " << ex.what() << std::endl;
--- a/irccdctl/watch_cli.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/irccdctl/watch_cli.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -177,9 +177,9 @@ void get_event(ctl::controller& ctl, std::string fmt) { - ctl.recv([&ctl, fmt] (auto code, auto message) { + ctl.read([&ctl, fmt] (auto code, auto message) { if (code) - throw boost::system::system_error(code); + throw std::system_error(code); const auto event = json_util::parser(message).get<std::string>("event"); const auto it = events.find(event ? *event : "");
--- a/libcommon/CMakeLists.txt Fri Apr 06 22:06:07 2018 +0200 +++ b/libcommon/CMakeLists.txt Tue Apr 10 21:20:30 2018 +0200 @@ -22,15 +22,23 @@ set( HEADERS + ${libcommon_SOURCE_DIR}/irccd/acceptor.hpp ${libcommon_SOURCE_DIR}/irccd/config.hpp + ${libcommon_SOURCE_DIR}/irccd/connector.hpp ${libcommon_SOURCE_DIR}/irccd/fs_util.hpp ${libcommon_SOURCE_DIR}/irccd/ini.hpp ${libcommon_SOURCE_DIR}/irccd/ini_util.hpp ${libcommon_SOURCE_DIR}/irccd/json_util.hpp - ${libcommon_SOURCE_DIR}/irccd/network_stream.hpp ${libcommon_SOURCE_DIR}/irccd/options.hpp + ${libcommon_SOURCE_DIR}/irccd/socket_acceptor.hpp + ${libcommon_SOURCE_DIR}/irccd/socket_connector.hpp + ${libcommon_SOURCE_DIR}/irccd/socket_stream.hpp + ${libcommon_SOURCE_DIR}/irccd/stream.hpp ${libcommon_SOURCE_DIR}/irccd/string_util.hpp ${libcommon_SOURCE_DIR}/irccd/system.hpp + ${libcommon_SOURCE_DIR}/irccd/tls_acceptor.hpp + ${libcommon_SOURCE_DIR}/irccd/tls_connector.hpp + ${libcommon_SOURCE_DIR}/irccd/tls_stream.hpp ${libcommon_SOURCE_DIR}/irccd/xdg.hpp )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/irccd/acceptor.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,77 @@ +/* + * acceptor.hpp -- abstract stream acceptor interface + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_COMMON_ACCEPTOR_HPP +#define IRCCD_COMMON_ACCEPTOR_HPP + +/** + * \file acceptor.hpp + * \brief Abstract stream acceptor interface. + */ + +#include <functional> +#include <memory> +#include <system_error> + +namespace irccd { + +namespace io { + +class stream; + +/** + * \brief Accept completion handler. + */ +using accept_handler = std::function<void (std::error_code, std::shared_ptr<stream>)>; + +/** + * \brief Abstract stream acceptor interface. + * + * This class is used to wait a new client in an asynchronous manner. Derived + * classes must implement a non-blocking accept function. + */ +class acceptor { +public: + /** + * Default constructor. + */ + acceptor() = default; + + /** + * Virtual destructor defaulted. + */ + virtual ~acceptor() = default; + + /** + * Start asynchronous accept. + * + * Once the client is accepted, the original acceptor must be kept until it + * is destroyed. + * + * \pre another accept operation must not be running + * \pre handler != nullptr + * \param handler the handler + */ + virtual void accept(accept_handler handler) = 0; +}; + +} // !io + +} // !irccd + +#endif // !IRCCD_COMMON_ACCEPTOR_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/irccd/connector.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,79 @@ +/* + * connector.hpp -- abstract connection interface + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_COMMON_CONNECTOR_HPP +#define IRCCD_COMMON_CONNECTOR_HPP + +/** + * \file connector.hpp + * \brief Abstract connection interface. + */ + +#include <functional> +#include <memory> +#include <system_error> + +namespace irccd { + +namespace io { + +class stream; + +/** + * \brief Connect completion handler. + */ +using connect_handler = std::function<void (std::error_code, std::shared_ptr<stream>)>; + +/** + * \brief Abstract connection interface. + * + * This class is used to connect to a stream end point (usually sockets) in an + * asynchronous manner. + * + * Derived class must implement non-blocking connect function. + */ +class connector { +public: + /** + * Default constructor. + */ + connector() = default; + + /** + * Virtual destructor defaulted. + */ + virtual ~connector() = default; + + /** + * Start asynchronous connect. + * + * Once the client is connected, the original acceptor must be kept until it + * is destroyed. + * + * \pre another connect operation must not be running + * \pre handler != nullptr + * \param handler the handler + */ + virtual void connect(connect_handler handler) = 0; +}; + +} // !io + +} // !irccd + +#endif // !IRCCD_COMMON_CONNECTOR_HPP
--- a/libcommon/irccd/network_stream.hpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,331 +0,0 @@ -/* - * network_stream.hpp -- base shared network stream - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_COMMON_NETWORK_STREAM_HPP -#define IRCCD_COMMON_NETWORK_STREAM_HPP - -/** - * \file network_stream.hpp - * \brief Base shared network stream. - */ - -#include "sysconfig.hpp" - -#include <deque> -#include <functional> -#include <string> -#include <utility> - -#include <boost/asio.hpp> - -#if defined(HAVE_SSL) -# include <boost/asio/ssl.hpp> -#endif - -#include <json.hpp> - -namespace irccd { - -/** - * Read handler. - * - * Call this function when a receive operation has finished on success or - * failure. - */ -using network_recv_handler = std::function<void (boost::system::error_code, nlohmann::json)>; - -/** - * Send handler. - * - * Call this function when a send operation has finished on success or failure. - */ -using network_send_handler = std::function<void (boost::system::error_code)>; - -/** - * \brief Base shared network stream. - * - * This class can be used to perform I/O over a networking socket, it is - * implemented as asynchronous operations over Boost.Asio. - * - * All recv/send operations are placed in a queue and performed when possible. - */ -template <typename Socket> -class network_stream { -private: - using rbuffer_t = boost::asio::streambuf; - using rqueue_t = std::deque<network_recv_handler>; - using squeue_t = std::deque<std::pair<std::string, network_send_handler>>; - - Socket socket_; - rbuffer_t rbuffer_; - rqueue_t rqueue_; - squeue_t squeue_; - - // Decomposition functions. - std::string eat(boost::system::error_code&, std::size_t) noexcept; - nlohmann::json parse(boost::system::error_code&, const std::string&) noexcept; - - // I/O flushing. - void rflush(); - void sflush(); - - // Wrap async_read_until/async_write. - void do_recv(network_recv_handler); - void do_send(const std::string&, network_send_handler); - -public: - /** - * Construct the stream. - * - * \param args the arguments to pass to the Socket constructor - */ - template <typename... Args> - inline network_stream(Args&&... args) - : socket_(std::forward<Args>(args)...) - { - } - - /** - * Get the underlying socket. - * - * \return the socket - */ - inline const Socket& get_socket() const noexcept - { - return socket_; - } - - /** - * Overloaded function. - * - * \return the socket - */ - inline Socket& get_socket() noexcept - { - return socket_; - } - - /** - * Tells if receive operations are pending. - * - * \return true if receiving is in progress - */ - inline bool is_receiving() const noexcept - { - return !rqueue_.empty(); - } - - /** - * Tells if send operations are pending. - * - * \return true if sending is in progress - */ - inline bool is_sending() const noexcept - { - return !squeue_.empty(); - } - - /** - * Tells if there are any I/O pending. - * - * \return true if sending is in progress - */ - inline bool is_active() const noexcept - { - return is_receiving() || is_sending(); - } - - /** - * Request a receive operation. - * - * The handler must not throw exceptions and `this` must be valid in the - * lifetime of the handler. - * - * \pre handler != nullptr - * \param handler the handler - */ - void recv(network_recv_handler); - - /** - * Request a send operation. - * - * The handler must not throw exceptions and `this` must be valid in the - * lifetime of the handler. - * - * \pre json.is_object() - * \param json the json message - * \param handler the optional handler - */ - void send(nlohmann::json json, network_send_handler = nullptr); -}; - -template <typename Socket> -std::string network_stream<Socket>::eat(boost::system::error_code& code, std::size_t xfer) noexcept -{ - try { - std::string str( - boost::asio::buffers_begin(rbuffer_.data()), - boost::asio::buffers_begin(rbuffer_.data()) + xfer - 4 - ); - - // Only clean buffer here, so user can still try again later. - rbuffer_.consume(xfer); - code = make_error_code(boost::system::errc::success); - - return str; - } catch (...) { - code = make_error_code(boost::system::errc::not_enough_memory); - return ""; - } -} - -template <typename Socket> -nlohmann::json network_stream<Socket>::parse(boost::system::error_code& code, const std::string& data) noexcept -{ - try { - const auto json = nlohmann::json::parse(data); - - if (!json.is_object()) { - code = make_error_code(boost::system::errc::invalid_argument); - return nullptr; - } - - code = make_error_code(boost::system::errc::success); - - return json; - } catch (...) { - code = make_error_code(boost::system::errc::invalid_argument); - return nullptr; - } -} - -template <typename Socket> -void network_stream<Socket>::rflush() -{ - if (rqueue_.empty()) - return; - - do_recv([this] (auto code, auto json) noexcept { - if (rqueue_.front()) - rqueue_.front()(code, std::move(json)); - - rqueue_.pop_front(); - - if (!code) - rflush(); - }); -} - -template <typename Socket> -void network_stream<Socket>::sflush() -{ - if (squeue_.empty()) - return; - - do_send(squeue_.front().first, [this] (auto code) noexcept { - if (squeue_.front().second) - squeue_.front().second(code); - - squeue_.pop_front(); - - if (!code) - sflush(); - }); -} - -template <typename Socket> -void network_stream<Socket>::do_recv(network_recv_handler handler) -{ - boost::asio::async_read_until(socket_, rbuffer_, "\r\n\r\n", [this, handler] (auto code, auto xfer) noexcept { - if (code || xfer == 0U) { - handler(make_error_code(boost::system::errc::network_down), nullptr); - return; - } - - auto str = eat(code, xfer); - - if (code) - handler(std::move(code), nullptr); - - auto message = parse(code, str); - - handler(std::move(code), std::move(message)); - }); -} - -template <typename Socket> -void network_stream<Socket>::do_send(const std::string& str, network_send_handler handler) -{ - boost::asio::async_write(socket_, boost::asio::buffer(str), [handler] (auto code, auto xfer) noexcept { - if (code || xfer == 0U) - handler(make_error_code(boost::system::errc::network_down)); - else - handler(code); - }); -} - -template <typename Socket> -void network_stream<Socket>::recv(network_recv_handler handler) -{ - auto in_progress = !rqueue_.empty(); - - rqueue_.push_back(std::move(handler)); - - if (!in_progress) - rflush(); -} - -template <typename Socket> -void network_stream<Socket>::send(nlohmann::json json, network_send_handler handler) -{ - assert(json.is_object()); - - auto in_progress = !squeue_.empty(); - - squeue_.emplace_back(json.dump(0) + "\r\n\r\n", std::move(handler)); - - if (!in_progress) - sflush(); -} - -/** - * \brief Typedef for TCP/IP socket. - */ -using ip_network_stream = network_stream<boost::asio::ip::tcp::socket>; - -#if !defined(IRCCD_SYSTEM_WINDOWS) - -/** - * \brief Typedef for Unix socket. - */ -using local_network_stream = network_stream<boost::asio::local::stream_protocol::socket>; - -#endif // !IRCCD_SYSTEM_WINDOWS - -#if defined(HAVE_SSL) - -/** - * \brief Typedef for SSL sockets. - */ -using tls_network_stream = network_stream<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>; - -#endif // !HAVE_SSL - -} // !irccd - -#endif // IRCCD_COMMON_NETWORK_STREAM_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/irccd/socket_acceptor.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,160 @@ +/* + * socket_acceptor.hpp -- socket stream acceptor interface + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_COMMON_SOCKET_ACCEPTOR_HPP +#define IRCCD_COMMON_SOCKET_ACCEPTOR_HPP + +/** + * \file socket_acceptor.hpp + * \brief Socket stream acceptor interface. + */ + +#include <irccd/sysconfig.hpp> + +#include <memory> +#include <system_error> + +#include <boost/asio.hpp> + +#include "acceptor.hpp" +#include "socket_stream.hpp" + +namespace irccd { + +namespace io { + +/** + * \brief Socket stream acceptor interface. + * \tparam Protocol a Boost.Asio compatible protocol (e.g. ip::tcp) + */ +template <typename Protocol> +class socket_acceptor : public acceptor { +public: + /** + * Convenient acceptor alias. + */ + using acceptor = typename Protocol::acceptor; + + /** + * Convenient socket alias. + */ + using socket = typename Protocol::socket; + +private: + acceptor acceptor_; + +#if !defined(NDEBUG) + bool is_accepting_{false}; +#endif + +protected: + /** + * Helper to accept on the real underlying socket. + * + * \param socket the real socket + * \param handler the handler + */ + template <typename Socket, typename Handler> + void do_accept(Socket& socket, Handler handler); + +public: + /** + * Construct the socket_acceptor. + * + * \pre acceptor must be ready (is_open() returns true) + * \param acceptor the Boost.Asio acceptor + */ + inline socket_acceptor(acceptor acceptor) noexcept + : acceptor_(std::move(acceptor)) + { + assert(acceptor_.is_open()); + } + + /** + * Get the underlying acceptor. + * + * \return the acceptor + */ + inline const acceptor& get_acceptor() const noexcept + { + return acceptor_; + } + + /** + * Overloaded function. + * + * \return the acceptor + */ + inline acceptor& get_acceptor() noexcept + { + return acceptor_; + } + + /** + * \copydoc acceptor::accept + */ + void accept(accept_handler handler) override; +}; + +template <typename Protocol> +template <typename Socket, typename Handler> +void socket_acceptor<Protocol>::do_accept(Socket& socket, Handler handler) +{ +#if !defined(NDEBUG) + assert(!is_accepting_); +#endif + + acceptor_.async_accept(socket, [this, handler] (auto code) { +#if !defined(NDEBUG) + is_accepting_ = false; +#endif + handler(detail::convert(code)); + }); +} + +template <typename Protocol> +void socket_acceptor<Protocol>::accept(accept_handler handler) +{ + assert(handler); + + const auto client = std::make_shared<socket_stream<socket>>(acceptor_.get_io_service()); + + do_accept(client->get_socket(), [this, client, handler] (auto code) { + handler(std::move(code), code ? nullptr : std::move(client)); + }); +} + +/** + * Convenient TCP/IP acceptor type. + */ +using ip_acceptor = socket_acceptor<boost::asio::ip::tcp>; + +#if !defined(IRCCD_SYSTEM_WINDOWS) + +/** + * Convenient Unix acceptor type. + */ +using local_acceptor = socket_acceptor<boost::asio::local::stream_protocol>; + +#endif + +} // !io + +} // !irccd + +#endif // !IRCCD_COMMON_SOCKET_ACCEPTOR_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/irccd/socket_connector.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,177 @@ +/* + * socket_connector.hpp -- socket connection interface + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_COMMON_SOCKET_CONNECTOR_HPP +#define IRCCD_COMMON_SOCKET_CONNECTOR_HPP + +/** + * \file socket_connector.hpp + * \brief Socket connection interface. + */ + +#include <irccd/sysconfig.hpp> + +#include <vector> + +#include "connector.hpp" +#include "socket_stream.hpp" + +#include <boost/asio.hpp> + +namespace irccd { + +namespace io { + +/** + * \brief Socket connection interface. + * \tparam Protocol a Boost.Asio compatible protocol (e.g. ip::tcp) + */ +template <typename Protocol> +class socket_connector : public connector { +public: + /** + * Convenient endpoint alias. + */ + using endpoint = typename Protocol::endpoint; + + /** + * Convenient socket alias. + */ + using socket = typename Protocol::socket; + +private: + boost::asio::io_service& service_; + std::vector<endpoint> endpoints_; + +#if !defined(NDEBUG) + bool is_connecting_{false}; +#endif + +protected: + /** + * Start trying to connect to all endpoints. + * + * \param socket the underlying socket + * \param handler handler with `void f(std::error_code)` signature + */ + template <typename Socket, typename Handler> + void do_connect(Socket& socket, Handler handler); + +public: + /** + * Construct the socket connector with only one endpoint. + * + * \param service the service + * \param endpoint the unique endpoint + */ + inline socket_connector(boost::asio::io_service& service, endpoint endpoint) noexcept + : service_(service) + , endpoints_{std::move(endpoint)} + { + } + + /** + * Construct the socket connection. + * + * \param service the service + * \param eps the endpoints + */ + inline socket_connector(boost::asio::io_service& service, std::vector<endpoint> eps) noexcept + : service_(service) + , endpoints_(std::move(eps)) + { + } + + /** + * Get the underlying I/O service. + * + * \return the I/O service + */ + inline const boost::asio::io_service& get_io_service() const noexcept + { + return service_; + } + + /** + * Overloaded function. + * + * \return the I/O service + */ + inline boost::asio::io_service& get_io_service() noexcept + { + return service_; + } + + /** + * \copydoc connector::connect + */ + void connect(connect_handler handler); +}; + +template <typename Protocol> +template <typename Socket, typename Handler> +void socket_connector<Protocol>::do_connect(Socket& socket, Handler handler) +{ +#if !defined(NDEBUG) + assert(!is_connecting_); + is_connecting_ = true; +#endif + + boost::asio::async_connect(socket, endpoints_.begin(), endpoints_.end(), [this, handler] (auto code, auto ep) { +#if !defined(NDEBUG) + is_connecting_ = false; +#endif + + if (ep == endpoints_.end()) + handler(make_error_code(std::errc::host_unreachable)); + else + handler(detail::convert(code)); + }); +} + +template <typename Protocol> +void socket_connector<Protocol>::connect(connect_handler handler) +{ + assert(handler); + + const auto stream = std::make_shared<socket_stream<socket>>(service_); + + do_connect(stream->get_socket(), [handler, stream] (auto code) { + handler(code, code ? nullptr : std::move(stream)); + }); +} + +/** + * Convenient TCP/IP connector type. + */ +using ip_connector = socket_connector<boost::asio::ip::tcp>; + +#if !defined(IRCCD_SYSTEM_WINDOWS) + +/** + * Convenient Unix conncetor type. + */ +using local_connector = socket_connector<boost::asio::local::stream_protocol>; + +#endif + +} // !io + +} // !irccd + +#endif // !IRCCD_COMMON_SOCKET_CONNECTOR_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/irccd/socket_stream.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,246 @@ +/* + * socket_stream.hpp -- socket stream interface + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_COMMON_SOCKET_STREAM_HPP +#define IRCCD_COMMON_SOCKET_STREAM_HPP + +/** + * \file socket_stream.hpp + * \brief Socket stream interface. + */ + +#include <irccd/sysconfig.hpp> + +#include <cstddef> +#include <cassert> +#include <string> +#include <system_error> +#include <utility> + +#include <boost/asio.hpp> + +#include "stream.hpp" + +namespace irccd { + +namespace io { + +/** + * \cond HIDDEN_SYMBOLS + */ + +namespace detail { + +/** + * Convert boost::system::error_code to std. + * + * \param code the error code + * \return the std::error_code + */ +inline std::error_code convert(boost::system::error_code code) noexcept +{ + return std::error_code(code.value(), std::system_category()); +} + +} // !detail + +/** + * \endcond + */ + +/** + * \brief Socket implementation interface. + * \tparam Socket the Boost.Asio compatible socket. + * + * This class reimplements stream for Boost.Asio sockets. + */ +template <typename Socket> +class socket_stream : public stream { +private: + Socket socket_; + boost::asio::streambuf input_; + std::string output_; + +#if !defined(NDEBUG) + bool is_receiving{false}; + bool is_sending{false}; +#endif + + void handle_read(boost::system::error_code, std::size_t, read_handler); + void handle_write(boost::system::error_code, std::size_t, write_handler); + +public: + /** + * Create the socket stream. + * + * \param args the Socket constructor arguments + */ + template <typename... Args> + inline socket_stream(Args&&... args) + : socket_(std::forward<Args>(args)...) + { + } + + /** + * Get the underlying socket. + * + * \return the socket + */ + inline const Socket& get_socket() const noexcept + { + return socket_; + } + + /** + * Overloaded function + * + * \return the socket + */ + inline Socket& get_socket() noexcept + { + return socket_; + } + + /** + * \copydoc stream::read + */ + void read(read_handler handler) override; + + /** + * \copydoc stream::write + */ + void write(const nlohmann::json& json, write_handler handler) override; +}; + +template <typename Socket> +void socket_stream<Socket>::handle_read(boost::system::error_code code, + std::size_t xfer, + read_handler handler) +{ +#if !defined(NDEBUG) + is_receiving = false; +#endif + + if (xfer == 0U) { + handler(make_error_code(std::errc::not_connected), nullptr); + return; + } + if (code) { + handler(detail::convert(code), nullptr); + return; + } + + // 1. Convert the buffer safely. + std::string buffer; + + try { + buffer = std::string( + boost::asio::buffers_begin(input_.data()), + boost::asio::buffers_begin(input_.data()) + xfer - /* \r\n\r\n */ 4 + ); + + input_.consume(xfer); + } catch (const std::bad_alloc&) { + handler(make_error_code(std::errc::not_enough_memory), nullptr); + return; + } + + // 2. Convert to JSON. + nlohmann::json doc; + + try { + doc = nlohmann::json::parse(buffer); + } catch (const std::exception&) { + handler(make_error_code(std::errc::invalid_argument), nullptr); + return; + } + + if (!doc.is_object()) + handler(make_error_code(std::errc::invalid_argument), nullptr); + else + handler(std::error_code(), std::move(doc)); +} + +template <typename Socket> +void socket_stream<Socket>::handle_write(boost::system::error_code code, + std::size_t xfer, + write_handler handler) +{ +#if !defined(NDEBUG) + is_sending = false; +#endif + + if (xfer == 0) + handler(make_error_code(std::errc::not_connected)); + else + handler(detail::convert(code)); +} + +template <typename Socket> +void socket_stream<Socket>::read(read_handler handler) +{ +#if !defined(NDEBUG) + assert(!is_receiving); + assert(handler); + + is_receiving = true; +#endif + + boost::asio::async_read_until(get_socket(), input_, "\r\n\r\n", [this, handler] (auto code, auto xfer) { + handle_read(code, xfer, std::move(handler)); + }); +} + +template <typename Socket> +void socket_stream<Socket>::write(const nlohmann::json& json, write_handler handler) +{ +#if !defined(NDEBUG) + assert(!is_sending); + assert(handler); + + is_sending = true; +#endif + + output_ = json.dump(0) + "\r\n\r\n"; + + const auto buffer = boost::asio::buffer(output_.data(), output_.size()); + + boost::asio::async_write(get_socket(), buffer, [this, handler] (auto code, auto xfer) { + handle_write(code, xfer, std::move(handler)); + }); +} + +/** + * Convenient TCP/IP stream type. + */ +using ip_stream = socket_stream<boost::asio::ip::tcp::socket>; + +#if !defined(IRCCD_SYSTEM_WINDOWS) + +/** + * Convenient Unix stream type. + */ +using local_stream = socket_stream<boost::asio::local::stream_protocol::socket>; + +#endif + +} // !io + +} // !irccd + +#endif // !IRCCD_COMMON_SOCKET_STREAM_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/irccd/stream.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,91 @@ +/* + * stream.hpp -- abstract stream interface + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_COMMON_STREAM_HPP +#define IRCCD_COMMON_STREAM_HPP + +/** + * \file stream.hpp + * \brief Abstract stream interface. + */ + +#include <functional> +#include <system_error> + +#include "json.hpp" + +namespace irccd { + +namespace io { + +/** + * \brief Read completion handler. + */ +using read_handler = std::function<void (std::error_code, nlohmann::json)>; + +/** + * \brief Write completion handler. + */ +using write_handler = std::function<void (std::error_code)>; + +/** + * \brief Abstract stream interface + * + * Abstract I/O interface that allows reading/writing from a stream in an + * asynchronous manner. + * + * The derived classes must implement non-blocking read and write operations. + */ +class stream { +public: + /** + * Default constructor. + */ + stream() = default; + + /** + * Virtual destructor defaulted. + */ + virtual ~stream() = default; + + /** + * Start asynchronous read. + * + * \pre another read operation must not be running + * \pre handler != nullptr + * \param handler the handler + */ + virtual void read(read_handler handler) = 0; + + /** + * Start asynchronous write. + * + * \pre json.is_object() + * \pre another write operation must not be running + * \pre handler != nullptr + * \param json the JSON message + * \param handler the handler + */ + virtual void write(const nlohmann::json& json, write_handler handler) = 0; +}; + +} // !io + +} // !irccd + +#endif // !IRCCD_COMMON_STREAM_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/irccd/tls_acceptor.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,96 @@ +/* + * tls_acceptor.hpp -- TLS/SSL acceptors + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_COMMON_TLS_ACCEPTOR_HPP +#define IRCCD_COMMON_TLS_ACCEPTOR_HPP + +/** + * \file tls_acceptor.hpp + * \brief TLS/SSL acceptors. + */ + +#include <irccd/sysconfig.hpp> + +#if defined(HAVE_SSL) + +#include "socket_acceptor.hpp" +#include "tls_stream.hpp" + +namespace irccd { + +namespace io { + +/** + * \brief TLS/SSL acceptors. + * \tparam Protocol a Boost.Asio compatible protocol (e.g. ip::tcp) + */ +template <typename Protocol = boost::asio::ip::tcp> +class tls_acceptor : public socket_acceptor<Protocol> { +private: + using socket = typename Protocol::socket; + + boost::asio::ssl::context context_; + +public: + /** + * Construct a secure layer transport server. + * + * \param context the SSL context + * \param args the socket_acceptor arguments + */ + template <typename... Args> + inline tls_acceptor(boost::asio::ssl::context context, Args&&... args) + : socket_acceptor<Protocol>(std::forward<Args>(args)...) + , context_(std::move(context)) + { + } + + /** + * \copydoc acceptor::accept + */ + void accept(accept_handler handler) override; +}; + +template <typename Protocol> +void tls_acceptor<Protocol>::accept(accept_handler handler) +{ + assert(handler); + + auto client = std::make_shared<tls_stream<socket>>(this->get_acceptor().get_io_service(), this->context_); + + socket_acceptor<Protocol>::do_accept(client->get_socket().lowest_layer(), [handler, client] (auto code) { + using boost::asio::ssl::stream_base; + + if (code) { + handler(code, nullptr); + return; + } + + client->get_socket().async_handshake(stream_base::server, [handler, client] (auto code) { + handler(detail::convert(code), code ? nullptr : std::move(client)); + }); + }); +} + +} // !io + +} // !irccd + +#endif // !HAVE_SSL + +#endif // !IRCCD_COMMON_TLS_ACCEPTOR_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/irccd/tls_connector.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,95 @@ +/* + * tls_connector.hpp -- TLS/SSL connectors + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_COMMON_TLS_CONNECTOR_HPP +#define IRCCD_COMMON_TLS_CONNECTOR_HPP + +/** + * \file tls_connector.hpp + * \brief TLS/SSL connectors. + */ + +#include <irccd/sysconfig.hpp> + +#if defined(HAVE_SSL) + +#include "socket_connector.hpp" +#include "tls_stream.hpp" + +namespace irccd { + +namespace io { + +/** + * \brief TLS/SSL connectors. + * \tparam Protocol a Boost.Asio compatible protocol (e.g. ip::tcp) + */ +template <typename Protocol = boost::asio::ip::tcp> +class tls_connector : public socket_connector<Protocol> { +private: + boost::asio::ssl::context context_; + +public: + /** + * Construct a secure layer transport server. + * + * \param context the SSL context + * \param args the arguments to socket_connector<Socket> constructor + */ + template <typename... Args> + inline tls_connector(boost::asio::ssl::context context, Args&&... args) + : socket_connector<Protocol>(std::forward<Args>(args)...) + , context_(std::move(context)) + { + } + + /** + * \copydoc socket_connector::connect + */ + void connect(connect_handler handler) override; +}; + +template <typename Protocol> +void tls_connector<Protocol>::connect(connect_handler handler) +{ + using boost::asio::ssl::stream_base; + using socket = typename Protocol::socket; + + assert(handler); + + const auto stream = std::make_shared<tls_stream<socket>>(this->get_io_service(), context_); + + socket_connector<Protocol>::do_connect(stream->get_socket().lowest_layer(), [this, handler, stream] (auto code) { + if (code) { + handler(code, nullptr); + return; + } + + stream->get_socket().async_handshake(stream_base::client, [handler, stream] (auto code) { + handler(detail::convert(code), code ? nullptr : std::move(stream)); + }); + }); +} + +} // !io + +} // !irccd + +#endif // !HAVE_SSL + +#endif // !IRCCD_COMMON_TLS_CONNECTOR_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/irccd/tls_stream.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,64 @@ +/* + * tls_stream.hpp -- TLS/SSL streams + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_COMMON_TLS_STREAM_HPP +#define IRCCD_COMMON_TLS_STREAM_HPP + +/** + * \file tls_stream.hpp + * \brief TLS/SSL streams. + */ + +#include <irccd/sysconfig.hpp> + +#if defined(HAVE_SSL) + +#include <boost/asio/ssl.hpp> + +#include "socket_stream.hpp" + +namespace irccd { + +namespace io { + +/** + * \brief TLS/SSL streams. + * \tparam Socket the Boost.Asio compatible socket. + */ +template <typename Socket = boost::asio::ip::tcp::socket> +class tls_stream : public socket_stream<boost::asio::ssl::stream<Socket>> { +public: + /** + * Constructor. + * + * \param args the arguments to boost::asio::ssl::stream<Socket> + */ + template <typename... Args> + inline tls_stream(Args&&... args) + : socket_stream<boost::asio::ssl::stream<Socket>>(std::forward<Args>(args)...) + { + } +}; + +} // !io + +} // !irccd + +#endif // !HAVE_SSL + +#endif // !IRCCD_COMMON_TLS_STREAM_HPP
--- a/libirccd-test/irccd/test/command_test.hpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd-test/irccd/test/command_test.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -21,12 +21,13 @@ #include <memory> -#include <irccd/daemon/ip_transport_server.hpp> +#include <irccd/socket_acceptor.hpp> +#include <irccd/socket_connector.hpp> + #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/logger.hpp> #include <irccd/daemon/service/transport_service.hpp> -#include <irccd/ctl/ip_connection.hpp> #include <irccd/ctl/controller.hpp> namespace irccd { @@ -51,16 +52,14 @@ /** * Result for request function. */ - using result = std::pair<nlohmann::json, boost::system::error_code>; + using result = std::pair<nlohmann::json, std::error_code>; boost::asio::io_service service_; - boost::asio::deadline_timer timer_; // daemon stuff. std::unique_ptr<irccd> daemon_; // controller stuff. - std::unique_ptr<ctl::connection> conn_; std::unique_ptr<ctl::controller> ctl_; command_test(); @@ -78,8 +77,8 @@ { result r; - ctl_->send(std::move(json)); - ctl_->recv([&] (auto result, auto message) { + ctl_->write(std::move(json)); + ctl_->read([&] (auto result, auto message) { r.first = message; r.second = result; }); @@ -93,8 +92,7 @@ template <typename... Commands> command_test<Commands...>::command_test() - : timer_(service_) - , daemon_(std::make_unique<irccd>(service_)) + : daemon_(std::make_unique<irccd>(service_)) { using boost::asio::ip::tcp; @@ -102,38 +100,42 @@ tcp::endpoint ep(tcp::v4(), 0); tcp::acceptor acc(service_, ep); - // Connect to the local bound port. - conn_ = std::make_unique<ctl::ip_connection>(service_, "127.0.0.1", acc.local_endpoint().port()); - ctl_ = std::make_unique<ctl::controller>(*conn_); + // Create controller and transport server. + ctl_ = std::make_unique<ctl::controller>( + std::make_unique<io::ip_connector>(service_, acc.local_endpoint())); + daemon_->transports().add(std::make_unique<transport_server>( + std::make_unique<io::ip_acceptor>(std::move(acc)))); // Add the server and the command. add<Commands...>(); daemon_->set_log(std::make_unique<silent_logger>()); - daemon_->transports().add(std::make_unique<ip_transport_server>(service_, std::move(acc))); + + // Wait for controller to connect. + boost::asio::deadline_timer timer(service_); - timer_.expires_from_now(boost::posix_time::seconds(10)); - timer_.async_wait([] (auto code) { - if (!code) - throw make_error_code(boost::system::errc::timed_out); + timer.expires_from_now(boost::posix_time::seconds(10)); + timer.async_wait([] (auto code) { + if (code && code != boost::asio::error::operation_aborted) + throw std::system_error(make_error_code(std::errc::timed_out)); }); bool connected = false; ctl_->connect([&] (auto code, auto) { + timer.cancel(); + if (code) - throw code; + throw std::system_error(code); connected = true; - timer_.cancel(); }); + /** + * Irccd will block indefinitely since transport_service will wait for any + * new client again, so we need to check with a boolean. + */ while (!connected) service_.poll(); - - service_.reset(); - - if (!connected) - throw std::runtime_error("unable to connect"); } } // !irccd
--- a/libirccd/CMakeLists.txt Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/CMakeLists.txt Tue Apr 10 21:20:30 2018 +0200 @@ -22,8 +22,6 @@ set( HEADERS - ${libirccd_SOURCE_DIR}/irccd/daemon/basic_transport_client.hpp - ${libirccd_SOURCE_DIR}/irccd/daemon/basic_transport_server.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/command.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/command/plugin_config_command.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/command/plugin_info_command.hpp @@ -54,10 +52,8 @@ ${libirccd_SOURCE_DIR}/irccd/daemon/command/server_reconnect_command.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/command/server_topic_command.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/dynlib_plugin.hpp - ${libirccd_SOURCE_DIR}/irccd/daemon/ip_transport_server.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/irccd.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/irc.hpp - ${libirccd_SOURCE_DIR}/irccd/daemon/local_transport_server.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/logger.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/plugin.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/rule.hpp @@ -71,7 +67,6 @@ ${libirccd_SOURCE_DIR}/irccd/daemon/transport_client.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/transport_server.hpp ${libirccd_SOURCE_DIR}/irccd/daemon/transport_util.hpp - $<$<BOOL:${HAVE_SSL}>:${libirccd_SOURCE_DIR}/irccd/daemon/tls_transport_server.hpp> ) set( @@ -119,7 +114,6 @@ ${libirccd_SOURCE_DIR}/irccd/daemon/transport_client.cpp ${libirccd_SOURCE_DIR}/irccd/daemon/transport_server.cpp ${libirccd_SOURCE_DIR}/irccd/daemon/transport_util.cpp - $<$<BOOL:${HAVE_SSL}>:${libirccd_SOURCE_DIR}/irccd/daemon/tls_transport_server.cpp> ) irccd_define_library(
--- a/libirccd/irccd/daemon/basic_transport_client.hpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,101 +0,0 @@ -/* - * basic_transport_client.hpp -- simple socket transport client - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_BASIC_TRANSPORT_CLIENT_HPP -#define IRCCD_BASIC_TRANSPORT_CLIENT_HPP - -#include <irccd/network_stream.hpp> - -#include "transport_client.hpp" - -namespace irccd { - -/** - * \brief Transport client for sockets. - * - * This class implements do_recv/do_send for Boost.Asio based socket streams. - */ -template <typename Socket> -class basic_transport_client : public transport_client { -private: - network_stream<Socket> stream_; - -protected: - /** - * \copydoc transport_client::do_recv - */ - void do_recv(network_recv_handler handler) override - { - const auto self = shared_from_this(); - - stream_.recv([this, self, handler] (auto msg, auto code) noexcept { - handler(std::move(msg), std::move(code)); - }); - } - - /** - * \copydoc transport_client::do_send - */ - void do_send(nlohmann::json json, network_send_handler handler) override - { - const auto self = shared_from_this(); - - stream_.send(std::move(json), [this, self, handler] (auto code) noexcept { - if (handler) - handler(std::move(code)); - }); - } - -public: - /** - * Construct the client. - * - * \param parent the parent - * \param args the argument to pass to the network_stream - */ - template <typename... Args> - inline basic_transport_client(transport_server& parent, Args&&... args) - : transport_client(parent) - , stream_(std::forward<Args>(args)...) - { - } - - /** - * Get the underlying stream. - * - * \return the stream - */ - inline const network_stream<Socket>& stream() const noexcept - { - return stream_; - } - - /** - * Overloaded function. - * - * \return the stream - */ - inline network_stream<Socket>& stream() noexcept - { - return stream_; - } -}; - -} // !irccd - -#endif // !IRCCD_BASIC_TRANSPORT_CLIENT_HPP
--- a/libirccd/irccd/daemon/basic_transport_server.hpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,99 +0,0 @@ -/* - * basic_transport_server.hpp -- simple socket transport servers - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_DAEMON_BASIC_TRANSPORT_SERVER_HPP -#define IRCCD_DAEMON_BASIC_TRANSPORT_SERVER_HPP - -/** - * \file basic_transport_server.hpp - * \brief Simple socket transport servers. - */ - -#include "basic_transport_client.hpp" -#include "transport_server.hpp" - -namespace irccd { - -/** - * \brief Basic implementation for IP/TCP and local sockets - * - * This class implements an accept function for: - * - * - boost::asio::ip::tcp - * - boost::asio::local::stream_protocol - */ -template <typename Protocol> -class basic_transport_server : public transport_server { -public: - /** - * Type for underlying socket. - */ - using socket_t = typename Protocol::socket; - - /** - * Type for underlying acceptor. - */ - using acceptor_t = typename Protocol::acceptor; - -protected: - /** - * The acceptor object. - */ - acceptor_t acceptor_; - -protected: - /** - * \copydoc transport_server::accept - */ - void do_accept(accept_handler handler) override; - -public: - /** - * Constructor with an acceptor in parameter. - * - * \pre acceptor.is_open() - * \param service the io service - * \param acceptor the already bound acceptor - */ - basic_transport_server(boost::asio::io_service& service, acceptor_t acceptor); -}; - -template <typename Protocol> -basic_transport_server<Protocol>::basic_transport_server(boost::asio::io_service& service, acceptor_t acceptor) - : transport_server(service) - , acceptor_(std::move(acceptor)) -{ - assert(acceptor_.is_open()); -} - -template <typename Protocol> -void basic_transport_server<Protocol>::do_accept(accept_handler handler) -{ - auto client = std::make_shared<basic_transport_client<socket_t>>(*this, acceptor_.get_io_service()); - - acceptor_.async_accept(client->stream().get_socket(), [this, client, handler] (auto code) { - if (code) - handler(std::move(code), nullptr); - else - handler(std::move(code), std::move(client)); - }); -} - -} // !irccd - -#endif // !IRCCD_DAEMON_BASIC_TRANSPORT_SERVER_HPP
--- a/libirccd/irccd/daemon/command/plugin_config_command.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/command/plugin_config_command.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -66,7 +66,7 @@ * * It's easier for the client to iterate over all. */ - client.send({ + client.write({ { "command", "plugin-config" }, { "variables", variables } });
--- a/libirccd/irccd/daemon/command/plugin_info_command.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/command/plugin_info_command.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -42,7 +42,7 @@ const auto plugin = irccd.plugins().require(*id); - client.send({ + client.write({ { "command", "plugin-info" }, { "author", plugin->get_author() }, { "license", plugin->get_license() },
--- a/libirccd/irccd/daemon/command/plugin_list_command.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/command/plugin_list_command.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -37,7 +37,7 @@ for (const auto& plugin : irccd.plugins().list()) list += plugin->get_name(); - client.send({ + client.write({ { "command", "plugin-list" }, { "list", list } });
--- a/libirccd/irccd/daemon/command/rule_info_command.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/command/rule_info_command.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -43,7 +43,7 @@ auto json = rule_util::to_json(irccd.rules().require(*index)); json.push_back({"command", "rule-info"}); - client.send(std::move(json)); + client.write(std::move(json)); } } // !irccd
--- a/libirccd/irccd/daemon/command/rule_list_command.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/command/rule_list_command.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -38,7 +38,7 @@ for (const auto& rule : irccd.rules().list()) array.push_back(rule_util::to_json(rule)); - client.send({ + client.write({ { "command", "rule-list" }, { "list", std::move(array) } });
--- a/libirccd/irccd/daemon/command/server_info_command.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/command/server_info_command.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -64,7 +64,7 @@ if (server->get_flags() & server::ssl_verify) response.push_back({"sslVerify", true}); - client.send(response); + client.write(response); } } // !irccd
--- a/libirccd/irccd/daemon/command/server_list_command.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/command/server_list_command.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -38,7 +38,7 @@ for (const auto& server : irccd.servers().servers()) list.push_back(server->get_name()); - client.send({ + client.write({ { "command", "server-list" }, { "list", std::move(list) } });
--- a/libirccd/irccd/daemon/ip_transport_server.hpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/* - * ip_transport_server.hpp -- server side transports (TCP/IP support) - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_DAEMON_IP_TRANSPORT_SERVER_HPP -#define IRCCD_DAEMON_IP_TRANSPORT_SERVER_HPP - -/** - * \file ip_transport_server.hpp - * \brief Server side transports (TCP/IP support). - */ - -#include "basic_transport_server.hpp" - -namespace irccd { - -/** - * Convenient type for IP/TCP - */ -using ip_transport_server = basic_transport_server<boost::asio::ip::tcp>; - -} // !irccd - -#endif // !IRCCD_DAEMON_IP_TRANSPORT_SERVER_HPP
--- a/libirccd/irccd/daemon/irc.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/irc.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -108,7 +108,9 @@ if (input_.empty()) return; - do_recv(buffer_, [this] (auto code, auto message) { + auto self = shared_from_this(); + + do_recv(buffer_, [this, self] (auto code, auto message) { if (input_.front()) input_.front()(code, std::move(message)); @@ -124,7 +126,9 @@ if (output_.empty()) return; - do_send(output_.front().first, [this] (auto code) { + auto self = shared_from_this(); + + do_send(output_.front().first, [this, self] (auto code) { if (output_.front().second) output_.front().second(code);
--- a/libirccd/irccd/daemon/irc.hpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/irc.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -356,7 +356,7 @@ /** * \brief Abstract connection to a server. */ -class connection { +class connection : public std::enable_shared_from_this<connection> { public: /** * Handler for connecting. @@ -373,6 +373,11 @@ */ using send_t = std::function<void (boost::system::error_code)>; + /** + * Convenient alias. + */ + using ptr = std::shared_ptr<connection>; + private: using buffer_t = boost::asio::streambuf; using input_t = std::deque<recv_t>; @@ -411,12 +416,12 @@ */ virtual void do_send(const std::string& data, send_t handler) noexcept = 0; -public: /** * Default constructor. */ connection() = default; +public: /** * Virtual destructor defaulted. */ @@ -478,7 +483,6 @@ */ void do_send(const std::string& data, send_t handler) noexcept override; -public: /** * Constructor. * @@ -489,6 +493,19 @@ , resolver_(service) { } + +public: + /** + * Wrap the connection as shared ptr. + * + * \param args the tls_connection constructor arguments + * \return the shared_ptr connection + */ + template <typename... Args> + static inline std::shared_ptr<ip_connection> create(Args&&... args) + { + return std::shared_ptr<ip_connection>(new ip_connection(std::forward<Args>(args)...)); + } }; #if defined(HAVE_SSL) @@ -518,18 +535,30 @@ */ void do_send(const std::string& data, send_t handler) noexcept override; -public: /** * Constructor. * * \param service the io service */ - inline tls_connection(boost::asio::io_service& service) noexcept + inline tls_connection(boost::asio::io_service& service) : context_(boost::asio::ssl::context::sslv23) , socket_(service, context_) , resolver_(service) { } + +public: + /** + * Wrap the connection as shared ptr. + * + * \param args the tls_connection constructor arguments + * \return the shared_ptr connection + */ + template <typename... Args> + static inline std::shared_ptr<tls_connection> create(Args&&... args) + { + return std::shared_ptr<tls_connection>(new tls_connection(std::forward<Args>(args)...)); + } }; #endif // !HAVE_SSL
--- a/libirccd/irccd/daemon/irccd.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/irccd.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -263,9 +263,9 @@ loaded_ = true; } -const boost::system::error_category& irccd_category() +const std::error_category& irccd_category() { - static const class category : public boost::system::error_category { + static const class category : public std::error_category { public: const char* name() const noexcept override { @@ -298,7 +298,7 @@ return category; } -boost::system::error_code make_error_code(irccd_error::error e) +std::error_code make_error_code(irccd_error::error e) { return {static_cast<int>(e), irccd_category()}; }
--- a/libirccd/irccd/daemon/irccd.hpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/irccd.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -213,7 +213,7 @@ /** * \brief Irccd error. */ -class irccd_error : public boost::system::system_error { +class irccd_error : public std::system_error { public: /** * \brief Irccd related errors. @@ -255,27 +255,23 @@ * * \return the singleton */ -const boost::system::error_category& irccd_category(); +const std::error_category& irccd_category(); /** * Create a boost::system::error_code from irccd_error::error enum. * * \param e the error code */ -boost::system::error_code make_error_code(irccd_error::error e); +std::error_code make_error_code(irccd_error::error e); } // !irccd -namespace boost { - -namespace system { +namespace std { template <> struct is_error_code_enum<irccd::irccd_error::error> : public std::true_type { }; -} // !system - -} // !boost +} // !std #endif // !IRCCD_DAEMON_IRCCD_HPP
--- a/libirccd/irccd/daemon/local_transport_server.hpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -/* - * local_transport_server.hpp -- server side transports (Unix support) - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_DAEMON_LOCAL_TRANSPORT_SERVER_HPP -#define IRCCD_DAEMON_LOCAL_TRANSPORT_SERVER_HPP - -/** - * \file local_transport_server.hpp - * \brief Server side transports (Unix support). - */ - -#include <irccd/sysconfig.hpp> - -#if !defined(IRCCD_SYSTEM_WINDOWS) - -#include "basic_transport_server.hpp" - -namespace irccd { - -/** - * Convenient type for UNIX local sockets. - */ -using local_transport_server = basic_transport_server<boost::asio::local::stream_protocol>; - -#endif // !_WIN32 - -} // !irccd - -#endif // !IRCCD_DAEMON_LOCAL_TRANSPORT_SERVER_HPP
--- a/libirccd/irccd/daemon/plugin.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/plugin.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -73,9 +73,9 @@ what_ = oss.str(); } -const boost::system::error_category& plugin_category() +const std::error_category& plugin_category() { - static const class category : public boost::system::error_category { + static const class category : public std::error_category { public: const char* name() const noexcept override { @@ -102,7 +102,7 @@ return category; } -boost::system::error_code make_error_code(plugin_error::error e) +std::error_code make_error_code(plugin_error::error e) { return {static_cast<int>(e), plugin_category()}; }
--- a/libirccd/irccd/daemon/plugin.hpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/plugin.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -552,7 +552,7 @@ /** * \brief Plugin error. */ -class plugin_error : public boost::system::system_error { +class plugin_error : public std::system_error { public: /** * \brief Plugin related errors. @@ -623,27 +623,23 @@ * * \return the singleton */ -const boost::system::error_category& server_category(); +const std::error_category& plugin_category(); /** * Create a boost::system::error_code from plugin_error::error enum. * * \param e the error code */ -boost::system::error_code make_error_code(plugin_error::error e); +std::error_code make_error_code(plugin_error::error e); } // !irccd -namespace boost { - -namespace system { +namespace std { template <> struct is_error_code_enum<irccd::plugin_error::error> : public std::true_type { }; -} // !system - -} // !boost +} // !std #endif // !IRCCD_DAEMON_PLUGIN_HPP
--- a/libirccd/irccd/daemon/rule.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/rule.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -56,9 +56,9 @@ match_set(events_, event); } -const boost::system::error_category& rule_category() +const std::error_category& rule_category() { - static const class category : public boost::system::error_category { + static const class category : public std::error_category { public: const char* name() const noexcept override { @@ -81,7 +81,7 @@ return category; } -boost::system::error_code make_error_code(rule_error::error e) +std::error_code make_error_code(rule_error::error e) { return {static_cast<int>(e), rule_category()}; }
--- a/libirccd/irccd/daemon/rule.hpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/rule.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -227,7 +227,7 @@ /** * \brief Rule error. */ -class rule_error : public boost::system::system_error { +class rule_error : public std::system_error { public: /** * \brief Rule related errors. @@ -254,27 +254,23 @@ * * \return the singleton */ -const boost::system::error_category& rule_category(); +const std::error_category& rule_category(); /** * Create a boost::system::error_code from rule_error::error enum. * * \param e the error code */ -boost::system::error_code make_error_code(rule_error::error e); +std::error_code make_error_code(rule_error::error e); } // !irccd -namespace boost { - -namespace system { +namespace std { template <> struct is_error_code_enum<irccd::rule_error::error> : public std::true_type { }; -} // !system - -} // !boost +} // !std #endif // !IRCCD_DAEMON_RULE_HPP
--- a/libirccd/irccd/daemon/server.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/server.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -499,7 +499,7 @@ if (flags_ & ssl) { #if defined(HAVE_SSL) - conn_ = std::make_unique<irc::tls_connection>(service_); + conn_ = irc::tls_connection::create(service_); #else /* * If SSL is not compiled in, the caller is responsible of not setting @@ -508,7 +508,7 @@ assert(!(flags_ & ssl)); #endif } else - conn_ = std::make_unique<irc::ip_connection>(service_); + conn_ = irc::ip_connection::create(service_); state_ = state_t::connecting; conn_->connect(host_, std::to_string(port_), [this] (auto code) { @@ -676,9 +676,9 @@ { } -const boost::system::error_category& server_category() +const std::error_category& server_category() { - static const class category : public boost::system::error_category { + static const class category : public std::error_category { public: const char* name() const noexcept override { @@ -735,7 +735,7 @@ return category; } -boost::system::error_code make_error_code(server_error::error e) +std::error_code make_error_code(server_error::error e) { return {static_cast<int>(e), server_category()}; }
--- a/libirccd/irccd/daemon/server.hpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/server.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -437,7 +437,7 @@ // Misc. boost::asio::io_service& service_; boost::asio::deadline_timer timer_; - std::unique_ptr<irc::connection> conn_; + std::shared_ptr<irc::connection> conn_; std::int8_t recocur_{0}; std::map<std::string, std::set<std::string>> names_map_; std::map<std::string, whois_info> whois_map_; @@ -902,7 +902,7 @@ /** * \brief Server error. */ -class server_error : public boost::system::system_error { +class server_error : public std::system_error { public: /** * \brief Server related errors. @@ -986,27 +986,23 @@ * * \return the singleton */ -const boost::system::error_category& server_category(); +const std::error_category& server_category(); /** * Create a boost::system::error_code from server_error::error enum. * * \param e the error code */ -boost::system::error_code make_error_code(server_error::error e); +std::error_code make_error_code(server_error::error e); } // !irccd -namespace boost { - -namespace system { +namespace std { template <> struct is_error_code_enum<irccd::server_error::error> : public std::true_type { }; -} // !system - -} // !boost +} // !std #endif // !IRCCD_DAEMON_SERVER_HPP
--- a/libirccd/irccd/daemon/service/transport_service.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/service/transport_service.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -23,7 +23,6 @@ #include <irccd/json_util.hpp> #include <irccd/daemon/command.hpp> -#include <irccd/daemon/ip_transport_server.hpp> #include <irccd/daemon/irccd.hpp> #include <irccd/daemon/logger.hpp> #include <irccd/daemon/transport_util.hpp> @@ -48,7 +47,7 @@ return; } - auto cmd = std::find_if(commands_.begin(), commands_.end(), [&] (const auto& cptr) { + const auto cmd = std::find_if(commands_.begin(), commands_.end(), [&] (const auto& cptr) { return cptr->get_name() == *name; }); @@ -57,7 +56,7 @@ else { try { (*cmd)->exec(irccd_, *tc, object); - } catch (const boost::system::system_error& ex) { + } catch (const std::system_error& ex) { tc->error(ex.code(), (*cmd)->get_name()); } catch (const std::exception& ex) { irccd_.get_log().warning() << "transport: unknown error not reported" << std::endl; @@ -68,19 +67,22 @@ void transport_service::do_recv(std::shared_ptr<transport_client> tc) { - tc->recv([this, tc] (auto code, auto json) { - switch (code.value()) { - case boost::system::errc::network_down: + tc->read([this, tc] (auto code, auto json) { + switch (static_cast<std::errc>(code.value())) { + case std::errc::not_connected: irccd_.get_log().info("transport: client disconnected"); break; - case boost::system::errc::invalid_argument: + case std::errc::invalid_argument: tc->error(irccd_error::invalid_message); break; default: - handle_command(tc, json); + // Other error may still happen. + if (!code) { + handle_command(tc, json); - if (tc->get_state() == transport_client::state_t::ready) - do_recv(std::move(tc)); + if (tc->get_state() == transport_client::state_t::ready) + do_recv(std::move(tc)); + } break; } @@ -90,9 +92,7 @@ void transport_service::do_accept(transport_server& ts) { ts.accept([this, &ts] (auto code, auto client) { - if (code) - irccd_.get_log().warning() << "transport: new client error: " << code.message() << std::endl; - else { + if (!code) { do_accept(ts); do_recv(std::move(client)); @@ -122,7 +122,7 @@ for (const auto& servers : servers_) for (const auto& client : servers->get_clients()) - client->send(json); + client->write(json); } void transport_service::load(const config& cfg) noexcept
--- a/libirccd/irccd/daemon/tls_transport_server.cpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/* - * tls_transport_server.cpp -- server side transports (SSL support) - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "tls_transport_server.hpp" - -#if defined(HAVE_SSL) - -namespace irccd { - -void tls_transport_server::do_handshake(std::shared_ptr<client_t> client, accept_handler handler) -{ - using boost::asio::ssl::stream_base; - - client->stream().get_socket().async_handshake(stream_base::server, [client, handler] (auto code) { - handler(std::move(code), std::move(client)); - }); -} - -tls_transport_server::tls_transport_server(boost::asio::io_service& service, acceptor_t acceptor, context_t context) - : ip_transport_server(service, std::move(acceptor)) - , context_(std::move(context)) -{ -} - -void tls_transport_server::do_accept(accept_handler handler) -{ - auto client = std::make_shared<client_t>(*this, acceptor_.get_io_service(), context_); - - acceptor_.async_accept(client->stream().get_socket().lowest_layer(), [this, client, handler] (auto code) { - if (code) - handler(std::move(code), nullptr); - else - do_handshake(std::move(client), std::move(handler)); - }); -} - -} // !irccd - -#endif // !HAVE_SSL
--- a/libirccd/irccd/daemon/tls_transport_server.hpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/* - * tls_transport_server.hpp -- server side transports (SSL support) - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_DAEMON_TLS_TRANSPORT_SERVER_HPP -#define IRCCD_DAEMON_TLS_TRANSPORT_SERVER_HPP - -/** - * \file tls_transport_server.hpp - * \brief Server side transports (SSL support). - */ - -#include <irccd/sysconfig.hpp> - -#if defined(HAVE_SSL) - -#include <boost/asio/ssl.hpp> - -#include "ip_transport_server.hpp" - -namespace irccd { - -/** - * \brief Secure layer implementation. - */ -class tls_transport_server : public ip_transport_server { -private: - using context_t = boost::asio::ssl::context; - using client_t = basic_transport_client<boost::asio::ssl::stream<socket_t>>; - - context_t context_; - - void do_handshake(std::shared_ptr<client_t>, accept_handler); - -protected: - /** - * \copydoc tcp_transport_server::do_accept - * - * This function does the same as tcp_transport_server::do_accept but it - * also perform a SSL handshake after a successful accept operation. - */ - void do_accept(accept_handler handler) override; - -public: - /** - * Construct a secure layer transport server. - * - * \param service the io service - * \param acceptor the acceptor - * \param context the SSL context - */ - tls_transport_server(boost::asio::io_service& service, acceptor_t acceptor, context_t context); -}; - -} // !irccd - -#endif // !HAVE_SSL - -#endif // !IRCCD_DAEMON_TLS_TRANSPORT_SERVER_HPP
--- a/libirccd/irccd/daemon/transport_client.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/transport_client.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -16,31 +16,45 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <cassert> - #include "transport_client.hpp" #include "transport_server.hpp" namespace irccd { -void transport_client::erase() +void transport_client::flush() { + if (queue_.empty()) + return; + const auto self = shared_from_this(); - state_ = state_t::closing; - parent_.get_service().post([this, self] () { - parent_.get_clients().erase(self); + stream_->write(queue_.front().first, [this, self] (auto code) { + if (queue_.front().second) + queue_.front().second(code); + + queue_.pop_front(); + + if (code) + erase(); + else + flush(); }); } -void transport_client::recv(network_recv_handler handler) +void transport_client::erase() +{ + state_ = state_t::closing; + parent_.get_clients().erase(shared_from_this()); +} + +void transport_client::read(io::read_handler handler) { assert(handler); - const auto self = shared_from_this(); + if (state_ != state_t::closing) { + const auto self = shared_from_this(); - if (state_ != state_t::closing) { - do_recv([this, self, handler] (auto code, auto msg) { + stream_->read([this, self, handler] (auto code, auto msg) { handler(code, msg); if (code) @@ -49,35 +63,29 @@ } } -void transport_client::send(nlohmann::json json, network_send_handler handler) +void transport_client::write(nlohmann::json json, io::write_handler handler) { - const auto self = shared_from_this(); + const auto in_progress = queue_.size() > 0; - if (state_ != state_t::closing) { - do_send(json, [this, self, handler] (auto code) { - if (handler) - handler(std::move(code)); - if (code) - erase(); - }); - } + queue_.emplace_back(std::move(json), std::move(handler)); + + if (!in_progress) + flush(); } -void transport_client::success(const std::string& cname, network_send_handler handler) +void transport_client::success(const std::string& cname, io::write_handler handler) { assert(!cname.empty()); - send({{ "command", cname }}, std::move(handler)); + write({{ "command", cname }}, std::move(handler)); } -void transport_client::error(boost::system::error_code code, network_send_handler handler) +void transport_client::error(std::error_code code, io::write_handler handler) { error(std::move(code), "", std::move(handler)); } -void transport_client::error(boost::system::error_code code, - std::string cname, - network_send_handler handler) +void transport_client::error(std::error_code code, std::string cname, io::write_handler handler) { assert(code); @@ -90,11 +98,13 @@ if (!cname.empty()) json["command"] = std::move(cname); - send(std::move(json), [this, handler] (auto code) { + const auto self = shared_from_this(); + + write(std::move(json), [this, handler, self] (auto code) { + erase(); + if (handler) handler(code); - - erase(); }); state_ = state_t::closing;
--- a/libirccd/irccd/daemon/transport_client.hpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/transport_client.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -19,7 +19,11 @@ #ifndef IRCCD_DAEMON_TRANSPORT_CLIENT_HPP #define IRCCD_DAEMON_TRANSPORT_CLIENT_HPP -#include <irccd/network_stream.hpp> +#include <cassert> +#include <deque> +#include <memory> + +#include <irccd/stream.hpp> namespace irccd { @@ -44,40 +48,25 @@ private: state_t state_{state_t::authenticating}; transport_server& parent_; - - void erase(); + std::shared_ptr<io::stream> stream_; + std::deque<std::pair<nlohmann::json, io::write_handler>> queue_; -protected: - /** - * Request a receive operation. - * - * The implementation must call the handler once the operation has finished - * even in case of errors. - * - * \param handler the non-null handler - */ - virtual void do_recv(network_recv_handler handler) = 0; - - /** - * Request a send operation. - * - * The implementation must call the handler once the operation has finished - * even in case of errors. - * - * \param json the json message to send - * \param handler the non-null handler - */ - virtual void do_send(nlohmann::json json, network_send_handler handler) = 0; + void flush(); + void erase(); public: /** * Constructor. * + * \pre stream != nullptr * \param server the parent + * \param stream the I/O stream */ - inline transport_client(transport_server& server) noexcept + inline transport_client(transport_server& server, std::shared_ptr<io::stream> stream) noexcept : parent_(server) + , stream_(std::move(stream)) { + assert(stream_); } /** @@ -130,13 +119,15 @@ * * Possible error codes: * - * - boost::system::errc::network_down in case of errors, - * - boost::system::errc::invalid_argument if the JSON message is invalid. + * - std::errc::network_down in case of errors, + * - std::errc::invalid_argument if the JSON message is invalid, + * - std::errc::not_enough_memory in case of memory failure. * * \pre handler != nullptr * \param handler the handler + * \warning Another read operation MUST NOT be running. */ - void recv(network_recv_handler handler); + void read(io::read_handler handler); /** * Start sending if not closed. @@ -145,18 +136,21 @@ * * - boost::system::errc::network_down in case of errors, * + * \pre json.is_object() * \param json the json message * \param handler the optional handler + * \note If a write operation is running, it is postponed once ready. */ - void send(nlohmann::json json, network_send_handler handler = nullptr); + void write(nlohmann::json json, io::write_handler handler = nullptr); /** * Convenient success message. * - * \param cname the command name + * \param command the command name * \param handler the optional handler + * \note If a write operation is running, it is postponed once ready. */ - void success(const std::string& cname, network_send_handler handler = nullptr); + void success(const std::string& command, io::write_handler handler = nullptr); /** * Send an error code to the client. @@ -164,20 +158,20 @@ * \pre code is not 0 * \param code the error code * \param handler the optional handler + * \note If a write operation is running, it is postponed once ready. */ - void error(boost::system::error_code code, network_send_handler handler = nullptr); + void error(std::error_code code, io::write_handler handler = nullptr); /** * Send an error code to the client. * * \pre code is not 0 * \param code the error code - * \param cname the command name + * \param command the command name * \param handler the optional handler + * \note If a write operation is running, it is postponed once ready. */ - void error(boost::system::error_code code, - std::string cname, - network_send_handler handler = nullptr); + void error(std::error_code code, std::string command, io::write_handler handler = nullptr); }; } // !irccd
--- a/libirccd/irccd/daemon/transport_server.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/transport_server.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -33,7 +33,7 @@ assert(client); assert(handler); - client->recv([this, client, handler] (auto code, auto message) { + client->read([this, client, handler] (auto code, auto message) { if (code) { handler(std::move(code), std::move(client)); return; @@ -50,6 +50,7 @@ client->error(irccd_error::invalid_auth); code = irccd_error::invalid_auth; } else { + clients_.insert(client); client->set_state(transport_client::state_t::ready); client->success("auth"); code = irccd_error::no_error; @@ -77,12 +78,16 @@ #endif }); - client->send(greetings, [this, client, handler] (auto code) { - if (code) + client->write(greetings, [this, client, handler] (auto code) { + if (code) { handler(std::move(code), std::move(client)); - else if (!password_.empty()) + return; + } + + if (!password_.empty()) do_auth(std::move(client), std::move(handler)); else { + clients_.insert(client); client->set_state(transport_client::state_t::ready); handler(std::move(code), std::move(client)); } @@ -91,15 +96,16 @@ void transport_server::accept(accept_handler handler) { - assert(handler); + acceptor_->accept([this, handler] (auto code, auto stream) { + if (code) { + handler(code, nullptr); + return; + } - do_accept([this, handler] (auto code, auto client) { - if (code) - handler(std::move(code), nullptr); - else { - clients_.insert(client); - do_greetings(std::move(client), std::move(handler)); - } + do_greetings( + std::make_shared<transport_client>(*this, std::move(stream)), + std::move(handler) + ); }); } @@ -108,9 +114,9 @@ { } -const boost::system::error_category& transport_category() noexcept +const std::error_category& transport_category() noexcept { - static const class category : public boost::system::error_category { + static const class category : public std::error_category { public: const char* name() const noexcept override { @@ -151,7 +157,7 @@ return category; }; -boost::system::error_code make_error_code(transport_error::error e) noexcept +std::error_code make_error_code(transport_error::error e) noexcept { return {static_cast<int>(e), transport_category()}; }
--- a/libirccd/irccd/daemon/transport_server.hpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/transport_server.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -27,7 +27,7 @@ #include <unordered_set> #include <type_traits> -#include <boost/asio.hpp> +#include <irccd/acceptor.hpp> #include "transport_client.hpp" @@ -130,38 +130,29 @@ using client_set = std::unordered_set<std::shared_ptr<transport_client>>; /** - * Callback when a new client should be accepted. + * Accept completion handler. */ - using accept_handler = std::function<void ( - boost::system::error_code, - std::shared_ptr<transport_client> - )>; + using accept_handler = std::function<void (std::error_code, std::shared_ptr<transport_client>)>; private: - boost::asio::io_service& service_; client_set clients_; + std::unique_ptr<io::acceptor> acceptor_; std::string password_; void do_auth(std::shared_ptr<transport_client>, accept_handler); void do_greetings(std::shared_ptr<transport_client>, accept_handler); -protected: - /** - * Start accept operation, the implementation should not block and call - * the handler function on error or completion. - * - * \pre handler must not be null - * \param handler the handler function - */ - virtual void do_accept(accept_handler handler) = 0; - public: /** - * Default constructor. + * Constructor. + * + * \pre acceptor != nullptr + * \param acceptor the stream acceptor */ - inline transport_server(boost::asio::io_service& service) noexcept - : service_(service) + inline transport_server(std::unique_ptr<io::acceptor> acceptor) noexcept + : acceptor_(std::move(acceptor)) { + assert(acceptor_); } /** @@ -170,37 +161,17 @@ virtual ~transport_server() noexcept = default; /** - * Accept a new client into the transport server. + * Accept a client. * * Also perform greetings and authentication under the hood. On success, the * client is added into the server and is ready to use. * - * \pre accept != nullptr - * \param handler the handler + * \pre handler != nullptr + * \param handler the completion handler */ void accept(accept_handler handler); /** - * Get the io service. - * - * \return the service - */ - inline const boost::asio::io_service& get_service() const noexcept - { - return service_; - } - - /** - * Overloaded function. - * - * \return the service - */ - inline boost::asio::io_service& get_service() noexcept - { - return service_; - } - - /** * Get the clients. * * \return the clients @@ -244,7 +215,7 @@ /** * \brief Transport error. */ -class transport_error : public boost::system::system_error { +class transport_error : public std::system_error { public: /** * \brief Transport related errors. @@ -300,27 +271,23 @@ * * \return the singleton */ -const boost::system::error_category& transport_category() noexcept; +const std::error_category& transport_category() noexcept; /** * Create a boost::system::error_code from server_error::error enum. * * \param e the error code */ -boost::system::error_code make_error_code(transport_error::error e) noexcept; +std::error_code make_error_code(transport_error::error e) noexcept; } // !irccd -namespace boost { - -namespace system { +namespace std { template <> struct is_error_code_enum<irccd::transport_error::error> : public std::true_type { }; -} // !system - -} // !boost +} // !std #endif // !IRCCD_DAEMON_TRANSPORT_SERVER_HPP
--- a/libirccd/irccd/daemon/transport_util.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccd/irccd/daemon/transport_util.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -22,18 +22,14 @@ #include <irccd/ini_util.hpp> #include <irccd/string_util.hpp> - -#include <irccd/daemon/ip_transport_server.hpp> - -#if !defined(IRCCD_SYSTEM_WINDOWS) -# include <irccd/daemon/local_transport_server.hpp> -#endif +#include <irccd/socket_acceptor.hpp> #if defined(HAVE_SSL) -# include <irccd/daemon/tls_transport_server.hpp> +# include <irccd/tls_acceptor.hpp> #endif #include "transport_util.hpp" +#include "transport_server.hpp" using namespace boost::asio; using namespace boost::asio::ip; @@ -119,11 +115,13 @@ ctx.use_private_key_file(key, boost::asio::ssl::context::pem); ctx.use_certificate_file(cert, boost::asio::ssl::context::pem); - return std::make_unique<tls_transport_server>(service, std::move(acceptor), std::move(ctx)); + return std::make_unique<transport_server>( + std::make_unique<io::tls_acceptor<>>(std::move(ctx), std::move(acceptor))); #endif } - return std::make_unique<ip_transport_server>(service, std::move(acceptor)); + return std::make_unique<transport_server>( + std::make_unique<io::ip_acceptor>(std::move(acceptor))); } std::unique_ptr<transport_server> from_config_load_unix(io_service& service, const ini::section& sc) @@ -144,7 +142,8 @@ stream_protocol::endpoint endpoint(path); stream_protocol::acceptor acceptor(service, std::move(endpoint)); - return std::make_unique<local_transport_server>(service, std::move(acceptor)); + return std::make_unique<transport_server>( + std::make_unique<io::local_acceptor>(std::move(acceptor))); #else (void)service; (void)sc;
--- a/libirccdctl/CMakeLists.txt Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccdctl/CMakeLists.txt Tue Apr 10 21:20:30 2018 +0200 @@ -20,23 +20,14 @@ set( HEADERS - ${libirccdctl_SOURCE_DIR}/irccd/ctl/basic_connection.hpp ${libirccdctl_SOURCE_DIR}/irccd/ctl/controller.hpp - ${libirccdctl_SOURCE_DIR}/irccd/ctl/connection.hpp - ${libirccdctl_SOURCE_DIR}/irccd/ctl/ip_connection.hpp ) set( SOURCES ${libirccdctl_SOURCE_DIR}/irccd/ctl/controller.cpp - ${libirccdctl_SOURCE_DIR}/irccd/ctl/ip_connection.cpp ) -if (NOT IRCCD_SYSTEM_WINDOWS) - list(APPEND HEADERS ${libirccdctl_SOURCE_DIR}/irccd/ctl/local_connection.hpp) - list(APPEND SOURCES ${libirccdctl_SOURCE_DIR}/irccd/ctl/local_connection.cpp) -endif () - irccd_define_library( TARGET libirccdctl SOURCES
--- a/libirccdctl/irccd/ctl/basic_connection.hpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -/* - * basic_connection.hpp -- network based connection for controller - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_CTL_BASIC_CONNECTION_HPP -#define IRCCD_CTL_BASIC_CONNECTION_HPP - -/** - * \file basic_connection.hpp - * \brief Network based connection for controller. - */ - -#include <irccd/network_stream.hpp> - -#include "connection.hpp" - -namespace irccd { - -namespace ctl { - -/** - * \brief Network based connection for controller. - * - * This class implements recv and send functions for Boost.Asio based sockets, - * the subclasses only need to implement a connect function. - */ -template <typename Socket> -class basic_connection : public connection { -protected: - network_stream<Socket> stream_; - -public: - /** - * Construct the network connection. - * - * \param args the arguments to pass to the socket - */ - template <typename... Args> - inline basic_connection(Args&&... args) - : stream_(std::forward<Args>(args)...) - { - } - - /** - * Tells if the stream has pending actions. - * - * \return true if receiving/sending - */ - bool is_active() const noexcept - { - return stream_.is_active(); - } - - /** - * Implements connection::recv using boost::asio::async_read_until. - * - * \param handler the handler - */ - void recv(network_recv_handler handler) override - { - stream_.recv(std::move(handler)); - } - - /** - * Implements connection::send using boost::asio::async_write. - * - * \param json the JSON message - * \param handler the handler - */ - void send(nlohmann::json json, network_send_handler handler) override - { - stream_.send(std::move(json), std::move(handler)); - } -}; - -} // !ctl - -} // !irccd - -#endif // !IRCCD_CTL_BASIC_CONNECTION_HPP
--- a/libirccdctl/irccd/ctl/connection.hpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/* - * connection.hpp -- abstract connection for irccdctl - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_CTL_CONNECTION_HPP -#define IRCCD_CTL_CONNECTION_HPP - -/** - * \file connection.hpp - * \brief Abstract connection for irccdctl. - */ - -#include <boost/system/error_code.hpp> - -#include <json.hpp> - -namespace irccd { - -namespace ctl { - -/** - * \brief Abstract connection for irccdctl. - */ -class connection { -public: - /** - * Connect handler. - * - * Call the handler when the underlying protocol connection is complete. - */ - using connect_t = std::function<void (boost::system::error_code)>; - - /** - * Tells if operations are in progress. - */ - virtual bool is_active() const noexcept = 0; - - /** - * Connect to the daemon. - * - * \param handler the non-null handler - */ - virtual void connect(connect_t handler) = 0; - - /** - * Receive from irccd. - * - * \param handler the completion handler - */ - virtual void recv(network_recv_handler handler) = 0; - - /** - * Send to irccd. - * - * \param json the json object - * \param handler the completion handler - */ - virtual void send(nlohmann::json json, network_send_handler handler) = 0; -}; - -} // !ctl - -} // !irccd - -#endif // !IRCCD_CTL_CONNECTION_HPP
--- a/libirccdctl/irccd/ctl/controller.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccdctl/irccd/ctl/controller.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -27,34 +27,33 @@ #include <irccd/daemon/rule.hpp> #include "controller.hpp" -#include "connection.hpp" namespace irccd { namespace ctl { -void controller::authenticate(connect_t handler, nlohmann::json info) +void controller::authenticate(connect_handler handler, nlohmann::json info) { - auto cmd = nlohmann::json::object({ - { "command", "auth" }, - { "password", password_ } + const auto cmd = nlohmann::json::object({ + { "command", "auth" }, + { "password", password_ } }); - send(std::move(cmd), [handler, info, this] (auto code) { + write(cmd, [handler, info, this] (auto code) { if (code) { handler(std::move(code), nullptr); return; } - recv([handler, info] (auto code, auto) { + read([handler, info] (auto code, auto) { handler(std::move(code), std::move(info)); }); }); } -void controller::verify(connect_t handler) +void controller::verify(connect_handler handler) { - recv([handler, this] (auto code, auto message) { + read([handler, this] (auto code, auto message) { if (code) { handler(std::move(code), std::move(message)); return; @@ -77,25 +76,30 @@ }); } -void controller::connect(connect_t handler) +void controller::connect(connect_handler handler) { assert(handler); - conn_.connect([handler, this] (auto code) { + connector_->connect([handler, this] (auto code, auto stream) { if (code) handler(std::move(code), nullptr); - else + else { + stream_ = std::move(stream); verify(std::move(handler)); + } }); } -void controller::recv(network_recv_handler handler) +void controller::read(io::read_handler handler) { assert(handler); + assert(stream_); - // TODO: ensure connected. - conn_.recv([handler] (auto code, auto msg) { + auto stream = stream_; + + stream_->read([this, handler, stream] (auto code, auto msg) { if (code) { + stream_ = nullptr; handler(std::move(code), std::move(msg)); return; } @@ -119,12 +123,19 @@ }); } -void controller::send(nlohmann::json message, network_send_handler handler) +void controller::write(nlohmann::json message, io::write_handler handler) { assert(message.is_object()); + assert(stream_); - // TODO: ensure connected. - conn_.send(std::move(message), std::move(handler)); + auto stream = stream_; + + stream_->write(std::move(message), [this, stream, handler] (auto code) { + if (code) + stream_ = nullptr; + if (handler) + handler(std::move(code)); + }); } } // !ctl
--- a/libirccdctl/irccd/ctl/controller.hpp Fri Apr 06 22:06:07 2018 +0200 +++ b/libirccdctl/irccd/ctl/controller.hpp Tue Apr 10 21:20:30 2018 +0200 @@ -24,14 +24,15 @@ * \brief Main irccdctl interface. */ -#include <irccd/network_stream.hpp> +#include <cassert> + +#include <irccd/connector.hpp> +#include <irccd/stream.hpp> namespace irccd { namespace ctl { -class connection; - /** * \brief Main irccdctl interface. * @@ -43,50 +44,50 @@ * * It is implemented in mind that connection are asynchronous even though this * is not necessary. - * - * \see connection - * \see network_connection - * \see local_connection - * \see ip_connection - * \see tls_connection */ class controller { public: /** - * Connection handler. + * Connection completion handler. * * This callback is called when connection has been completed or failed. In * both case, the error code is set and the JSON object may contain the * irccd program information. */ - using connect_t = std::function<void (boost::system::error_code, nlohmann::json)>; + using connect_handler = std::function<void (std::error_code, nlohmann::json)>; private: - connection& conn_; + std::unique_ptr<io::connector> connector_; + std::shared_ptr<io::stream> stream_; std::string password_; - void authenticate(connect_t, nlohmann::json); - void verify(connect_t); + void authenticate(connect_handler, nlohmann::json); + void verify(connect_handler); public: /** * Construct the controller with its connection. * + * \pre connector != nullptr + * \ * \note no connect attempt is done */ - inline controller(connection& conn) noexcept - : conn_(conn) + inline controller(std::unique_ptr<io::connector> connector) noexcept + : connector_(std::move(connector)) { + assert(connector_); } +#if 0 + /** * Access the underlying connection. * * \return the connection */ - inline const connection& get_conn() const noexcept + inline const connection& get_stream() const noexcept { - return conn_; + return *stream_; } /** @@ -94,10 +95,11 @@ * * \return the connection */ - inline connection& get_conn() noexcept + inline connection& get_stream() noexcept { - return conn_; + return *stream_; } +#endif /** * Get the optional password set. @@ -128,7 +130,7 @@ * \pre handler != nullptr * \param handler the handler */ - void connect(connect_t handler); + void connect(connect_handler handler); /** * Queue a receive operation, if receive operations are already running, it @@ -137,7 +139,7 @@ * \pre handler != nullptr * \param handler the recv handler */ - void recv(network_recv_handler handler); + void read(io::read_handler handler); /** * Queue a send operation, if receive operations are already running, it is @@ -147,7 +149,7 @@ * \param message the JSON message * \param handler the optional completion handler */ - void send(nlohmann::json message, network_send_handler handler = nullptr); + void write(nlohmann::json message, io::write_handler handler = nullptr); }; } // !ctl
--- a/libirccdctl/irccd/ctl/ip_connection.cpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -/* - * ip_connection.cpp -- TCP/IP and SSL connections - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "ip_connection.hpp" - -using resolver = boost::asio::ip::tcp::resolver; - -namespace irccd { - -namespace ctl { - -namespace { - -template <typename Socket> -void do_connect(Socket& socket, resolver::iterator it, connection::connect_t handler) -{ - socket.close(); - socket.async_connect(*it, [&socket, it, handler] (auto code) mutable { - if (code && ++it != resolver::iterator()) - do_connect(socket, it, std::move(handler)); - else - handler(code); - }); -} - -template <typename Socket> -void do_resolve(const std::string& host, - const std::string& port, - Socket& socket, - resolver& resolver, - connection::connect_t handler) -{ - resolver::query query(host, port); - - resolver.async_resolve(query, [&socket, handler] (auto code, auto it) { - if (code) - handler(code); - else - do_connect(socket, it, std::move(handler)); - }); -} - -} // !namespace - -void ip_connection::connect(connect_t handler) -{ - do_resolve(host_, std::to_string(port_), stream_.get_socket(), resolver_, std::move(handler)); -} - -#if defined(HAVE_SSL) - -void tls_connection::handshake(connect_t handler) -{ - stream_.get_socket().async_handshake(boost::asio::ssl::stream_base::client, [handler] (auto code) { - handler(code); - }); -} - -void tls_connection::connect(connect_t handler) -{ - const auto port = std::to_string(port_); - - do_resolve(host_, port, stream_.get_socket().lowest_layer(), resolver_, [handler, this] (auto code) { - if (code) - handler(code); - else - handshake(std::move(handler)); - }); -} - -#endif // !HAVE_SSL - -} // !ctl - -} // !irccd
--- a/libirccdctl/irccd/ctl/ip_connection.hpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,125 +0,0 @@ -/* - * ip_connection.hpp -- TCP/IP and SSL connections - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_CTL_IP_CONNECTION_HPP -#define IRCCD_CTL_IP_CONNECTION_HPP - -/** - * \file ip_connection.hpp - * \brief TCP/IP and SSL connections. - */ - -#include <irccd/sysconfig.hpp> - -#include <string> -#include <cstdint> - -#include "basic_connection.hpp" - -namespace irccd { - -namespace ctl { - -/** - * \brief Common class for both ip and tls connections. - */ -template <typename Socket> -class basic_ip_connection : public basic_connection<Socket> { -protected: - boost::asio::ip::tcp::resolver resolver_; - std::string host_; - std::uint16_t port_; - -public: - /** - * Construct the ip connection. - * - * The socket is created as invoked like this: - * - * socket_(service_, std::forward<Args>(args)...) - * - * \param service the io service - * \param host the host - * \param port the port number - * \param args extra arguments (except service) to pass to the socket constructor - */ - template <typename... Args> - inline basic_ip_connection(boost::asio::io_service& service, std::string host, std::uint16_t port, Args&&... args) - : basic_connection<Socket>(service, std::forward<Args>(args)...) - , resolver_(service) - , host_(std::move(host)) - , port_(std::move(port)) - { - } -}; - -/** - * \brief Raw TCP/IP connection. - */ -class ip_connection : public basic_ip_connection<boost::asio::ip::tcp::socket> { -public: - /** - * Inherited constructor. - */ - using basic_ip_connection::basic_ip_connection; - - /** - * \copydoc connection::connect - */ - void connect(connect_t handler); -}; - -#if defined(HAVE_SSL) - -/** - * \brief Secure layer connection. - */ -class tls_connection : public basic_ip_connection<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> { -private: - void handshake(connect_t); - -public: - /** - * Construct the TLS connection. - * - * \param service the io service - * \param context the context - * \param host the host - * \param port the port number - */ - inline tls_connection(boost::asio::io_service& service, - boost::asio::ssl::context& context, - std::string host, - std::uint16_t port) - : basic_ip_connection(service, std::move(host), port, context) - { - } - - /** - * \copydoc connection::connect - */ - void connect(connect_t handler); -}; - -#endif // !HAVE_SSL - -} // !ctl - -} // !irccd - -#endif // !IRCCD_CTL_IP_CONNECTION_HPP
--- a/libirccdctl/irccd/ctl/local_connection.cpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/* - * local_connection.cpp -- unix domain connection for irccdctl - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "local_connection.hpp" - -#if !defined(IRCCD_SYSTEM_WINDOWS) - -namespace irccd { - -namespace ctl { - -void local_connection::connect(connect_t handler) -{ - using endpoint = boost::asio::local::stream_protocol::endpoint; - - stream_.get_socket().async_connect(endpoint(path_), std::move(handler)); -} - -} // !ctl - -} // !irccd - -#endif // !IRCCD_SYSTEM_WINDOWS
--- a/libirccdctl/irccd/ctl/local_connection.hpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/* - * local_connection.hpp -- unix domain connection for irccdctl - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_CTL_LOCAL_CONNECTION_HPP -#define IRCCD_CTL_LOCAL_CONNECTION_HPP - -/** - * \file local_connection.hpp - * \brief Unix domain connection for irccdctl. - */ - -#include "basic_connection.hpp" - -#if !defined(IRCCD_SYSTEM_WINDOWS) - -namespace irccd { - -namespace ctl { - -/** - * \brief Unix domain connection for irccdctl. - */ -class local_connection : public basic_connection<boost::asio::local::stream_protocol::socket> { -private: - std::string path_; - -public: - /** - * Construct the local connection. - * - * \param service the io_service - * \param path the path to the socket file - */ - inline local_connection(boost::asio::io_service& service, std::string path) noexcept - : basic_connection(service) - , path_(std::move(path)) - { - } - - /** - * Connect to the socket file. - * - * \param handler the handler - */ - void connect(connect_t handler) override; -}; - -} // !ctl - -} // !irccd - -#endif // !IRCCD_SYSTEM_WINDOWS - -#endif // !IRCCD_CTL_LOCAL_CONNECTION_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/data/test.crt Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDfjCCAmYCCQCZkHkS4lewTjANBgkqhkiG9w0BAQsFADCBgDELMAkGA1UEBhMC +RlIxDzANBgNVBAgMBkFsc2FjZTEQMA4GA1UEBwwHT2Jlcm5haTESMBAGA1UECgwJ +TWFsaWthbmlhMRUwEwYDVQQDDAxtYWxpa2FuaWEuZnIxIzAhBgkqhkiG9w0BCQEW +FG1hcmthbmRAbWFsaWthbmlhLmZyMB4XDTE4MDQxMDEyMDgwOFoXDTI4MDEwODEy +MDgwOFowgYAxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZBbHNhY2UxEDAOBgNVBAcM +B09iZXJuYWkxEjAQBgNVBAoMCU1hbGlrYW5pYTEVMBMGA1UEAwwMbWFsaWthbmlh +LmZyMSMwIQYJKoZIhvcNAQkBFhRtYXJrYW5kQG1hbGlrYW5pYS5mcjCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBANdlxYhxfgoJDabzqPHYyBcNkdl0tXO2 +WOw6kO7EpQ1n0a1sMlbuFncSyYUP9ggvzGsql3vZLKYxqoOXYkcae096RBin8bqB +ZIo6OCENxcOnIgsnkFHy9n+AtyQEJItg3/cf46BqdoMF2TW9Z2XCMCNw9IE1Bxj4 +qR2BwHK10YOUB6EQCkC3+i1aydD/Kyiiwmm+650IMQfAPjN9vW9qaQ9a7H0x98k3 +vKj5FWjiJtXVZxk920eyM42o/kbFT7jkmqFN7NPX5GjealCpq0Wg5G/XCKLy9j7Q +DU0HRNNFGxBrfZpjEpWy2kyE2l3Iy+UPNemACBM/xcScj4yJeK9D9xUCAwEAATAN +BgkqhkiG9w0BAQsFAAOCAQEAdOG9Jljyg6rOrm98opUxbR0fnT1IE5vbwvLsrIFP +TRfsMSEPYsTfN0X34SlA7twyeCCqEIeY1/RBGSByaTqTfTXSGOgNKk4ZUqmAIscc +tqlrHE44IMbMCaqphqlDBMx7dOY4C05AMHjNVXtkUGYWh/S8Fyure8DGfXh1Vop/ +1nwHThB7t/4oa3w993ODvfqVz6sI7OAJNvlGWWHKsDbovUZ6BtVgZJioeMK3Zvak +igDPydVBG4gW2NstD9kujMrveMOAjzxSnbzJezeD/jan1u2mxHkuNrjVHA8GBHO7 +hIgdAJuLsicCm4yGu6TzZ6NQ2+EP/BdCzNfbXeYRhx1F+w== +-----END CERTIFICATE-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/data/test.key Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA12XFiHF+CgkNpvOo8djIFw2R2XS1c7ZY7DqQ7sSlDWfRrWwy +Vu4WdxLJhQ/2CC/MayqXe9kspjGqg5diRxp7T3pEGKfxuoFkijo4IQ3Fw6ciCyeQ +UfL2f4C3JAQki2Df9x/joGp2gwXZNb1nZcIwI3D0gTUHGPipHYHAcrXRg5QHoRAK +QLf6LVrJ0P8rKKLCab7rnQgxB8A+M329b2ppD1rsfTH3yTe8qPkVaOIm1dVnGT3b +R7Izjaj+RsVPuOSaoU3s09fkaN5qUKmrRaDkb9cIovL2PtANTQdE00UbEGt9mmMS +lbLaTITaXcjL5Q816YAIEz/FxJyPjIl4r0P3FQIDAQABAoIBAQCX5I2qjRXrb7zv +2W1utodLMmeCaDm40oLceuRPa95UmLyUxfKtuJGhAF+ZdMrztPk7LTrLvDcvL2Dm +EO/d4j/IqRiyJmRhN/O1Qh7ouDSYpxhrs0ejcmj/Or5rKHy4yOTG+Pbk3Y6bEJm+ +usTaTljx9SqGnuVVZ6yiQSh7+9k2JfXu6MTMBwc0aWU+hCXNzuD7sWobkECJe5+D +kPVRTz4r9GHXrQ95EjB7DxW6aKJgzxUBEf1ajk3nZVMi1rRcO7UjSWhlfVpSajCZ +Fx5WcROoOnkVjR7r6dB5+3t2d6F1NfsZG7pnOYdxKz1EED47HLA3auOylHGdxlIa +IsruxvYVAoGBAP9M2AJmFNTU2AWem7ULclxoZmITuRksKRbVFQWAhnYFs4zq3NLE +Ak8PLaBEbypy3nw2n8BoQG7YjDu7ZduQaTITgDvQLZw+wyczb8oM7OktsHd4fFPo +GY/C+KcX78lEfNdXtJjDGogaEcaIZLu/uCEE513IKOycvHCIDb6qzQtXAoGBANf8 +7R+9omQoxmz2yH83IxPZLCVsmifzc/ZjlfEz6bJPQJvd6tD/xV1O58xHX2OQrv71 +eqJiR5hOToRK5V8U0pb+5DA8knJ8eTQbSIo9aMBXJkb7qoHfXpiBf8KNTQtPPr2p +5CkCeMbGHPXvpmaB7jK7k7k2sr2OwJB13OLT+rlzAoGBAPT9HsSehRr/7RTncXA4 +vdOjc6xmVNetIvkAHZ4cCewz6Gsv7hxrb/PCEvya7zqC5LG6EW8oG7zDHT8sw8AB +QpGFWScMzNgE+m0h1QLFiIrzB0Z+XoB+WBk29joSpE415L8ZMPrvLwSwT6J+vHvW +rLEy7Xt3Wp5EgihSxy3S5dUdAoGAfXj5Z88KX5UwcGyM3Rpyzj0DYFpO05aibyg7 +GvxFbsiLiADLQM7VIPeNwSZVS8npX0PMEjl1zgzvn/rCdRHrpLw+Y6dMjCWzY4nW +AjjaeaInImVhEEFq+r2AMs1TTJakpBnl6cz9zLuaZ2TpDfO9JMvDbX1RKL2dME7I +Gx16MfECgYEA1d9pO+4pW+iosdJJESG1VP6W3z6H7Gd7fBuVa8FJVcYbyfF+c0Vz +2gdDJOYXBH2pEvWrrpUKoNTpNT/ei3qruxfryIVlC769Xs5EWF9/kBwzdJID+JgV +/gNXEVnBqOs7DuJ3yXMWdS1j9W1+tFshYw00l9aCCI2YSqdd6lsfJzg= +-----END RSA PRIVATE KEY-----
--- a/tests/src/libcommon/CMakeLists.txt Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libcommon/CMakeLists.txt Tue Apr 10 21:20:30 2018 +0200 @@ -17,5 +17,5 @@ # add_subdirectory(fs-util) -add_subdirectory(network-stream) +add_subdirectory(io) add_subdirectory(string-util)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libcommon/io/CMakeLists.txt Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,23 @@ +# +# CMakeLists.txt -- CMake build system for irccd +# +# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> +# +# 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 appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +irccd_define_test( + NAME io + SOURCES main.cpp + LIBRARIES libcommon +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/src/libcommon/io/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -0,0 +1,223 @@ +/* + * main.cpp -- test io classes + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BOOST_TEST_MODULE "io" +#include <boost/test/unit_test.hpp> +#include <boost/mpl/list.hpp> + +#include <irccd/sysconfig.hpp> + +#include <irccd/socket_acceptor.hpp> +#include <irccd/socket_connector.hpp> +#include <irccd/socket_stream.hpp> + +#if defined(HAVE_SSL) +# include <irccd/tls_acceptor.hpp> +# include <irccd/tls_connector.hpp> +# include <irccd/tls_stream.hpp> +#endif // !HAVE_SSL + +using boost::asio::io_service; +using boost::asio::ip::tcp; + +#if defined(HAVE_SSL) +using boost::asio::ssl::context; +#endif + +#if !defined(IRCCD_SYSTEM_WINDOWS) +using boost::asio::local::stream_protocol; +#endif + +namespace irccd { + +class io_test { +public: + io_service service_; + + std::unique_ptr<io::acceptor> acceptor_; + std::unique_ptr<io::connector> connector_; + + std::shared_ptr<io::stream> stream1_; + std::shared_ptr<io::stream> stream2_; + + virtual std::unique_ptr<io::acceptor> create_acceptor() = 0; + + virtual std::unique_ptr<io::connector> create_connector() = 0; + + void init() + { + acceptor_ = create_acceptor(); + connector_ = create_connector(); + + acceptor_->accept([this] (auto code, auto stream) { + if (code) + throw std::system_error(code); + + stream1_ = std::move(stream); + }); + connector_->connect([this] (auto code, auto stream) { + if (code) + throw std::system_error(code); + + stream2_ = std::move(stream); + }); + + service_.run(); + service_.reset(); + } +}; + +class ip_io_test : public io_test { +private: + tcp::endpoint endpoint_; + +protected: + /** + * \copydoc io_test::create_acceptor + */ + std::unique_ptr<io::acceptor> create_acceptor() override + { + tcp::endpoint endpoint(tcp::v4(), 0U); + tcp::acceptor acceptor(service_, std::move(endpoint)); + + endpoint_ = acceptor.local_endpoint(); + + return std::make_unique<io::ip_acceptor>(std::move(acceptor)); + } + + /** + * \copydoc io_test::create_connector + */ + std::unique_ptr<io::connector> create_connector() override + { + return std::make_unique<io::ip_connector>(service_, endpoint_); + } +}; + +#if defined(HAVE_SSL) + +class ssl_io_test : public io_test { +private: + tcp::endpoint endpoint_; + +protected: + /** + * \copydoc io_test::create_acceptor + */ + std::unique_ptr<io::acceptor> create_acceptor() override + { + context context(context::sslv23); + + context.use_certificate_file(TESTS_SOURCE_DIR "/data/test.crt", context::pem); + context.use_private_key_file(TESTS_SOURCE_DIR "/data/test.key", context::pem); + + tcp::endpoint endpoint(tcp::v4(), 0U); + tcp::acceptor acceptor(service_, std::move(endpoint)); + + endpoint_ = acceptor.local_endpoint(); + + return std::make_unique<io::tls_acceptor<>>(std::move(context), std::move(acceptor)); + } + + /** + * \copydoc io_test::create_connector + */ + std::unique_ptr<io::connector> create_connector() override + { + return std::make_unique<io::tls_connector<>>(context(context::sslv23), service_, endpoint_); + } +}; + +#endif // !HAVE_SSL + +#if !defined(IRCCD_SYSTEM_WINDOWS) + +class local_io_test : public io_test { +public: + /** + * \copydoc io_test::create_acceptor + */ + std::unique_ptr<io::acceptor> create_acceptor() override + { + std::remove(CMAKE_BINARY_DIR "/tmp/io-test.sock"); + + stream_protocol::acceptor acceptor(service_, CMAKE_BINARY_DIR "/tmp/io-test.sock"); + + return std::make_unique<io::local_acceptor>(std::move(acceptor)); + } + + /** + * \copydoc io_test::create_connector + */ + std::unique_ptr<io::connector> create_connector() override + { + return std::make_unique<io::local_connector>(service_, CMAKE_BINARY_DIR "/tmp/io-test.sock"); + } +}; + +#endif // !IRCCD_SYSTEM_WINDOWS + +/** + * List of fixtures to tests. + */ +using list = boost::mpl::list< + ip_io_test +#if defined(HAVE_SSL) + , ssl_io_test +#endif +#if !defined(IRCCD_SYSTEM_WINDOWS) + , local_io_test +#endif +>; + +BOOST_AUTO_TEST_CASE_TEMPLATE(invalid_argument, Test, list) +{ + Test fixture; + + const nlohmann::json message{ + { "abc", 123 }, + { "def", 456 } + }; + + fixture.init(); + fixture.stream1_->read([] (auto code, auto message) { + BOOST_TEST(!code); + BOOST_TEST(message.is_object()); + BOOST_TEST(message["abc"].template get<int>() == 123); + BOOST_TEST(message["def"].template get<int>() == 456); + }); + fixture.stream2_->write(message, [] (auto code) { + BOOST_TEST(!code); + }); + fixture.service_.run(); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(network_down, Test, list) +{ + Test fixture; + + fixture.init(); + fixture.stream1_->read([] (auto code, auto message) { + BOOST_TEST(code.value() == static_cast<int>(std::errc::not_connected)); + BOOST_TEST(message.is_null()); + }); + fixture.stream2_ = nullptr; + fixture.service_.run(); +} + +} // !irccd
--- a/tests/src/libcommon/network-stream/CMakeLists.txt Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -# CMakeLists.txt -- CMake build system for irccd -# -# Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> -# -# 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 appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# - -irccd_define_test( - NAME network-stream - SOURCES main.cpp - LIBRARIES libcommon -)
--- a/tests/src/libcommon/network-stream/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,153 +0,0 @@ -/* - * main.cpp -- test network_stream class - * - * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define BOOST_TEST_MODULE "network-stream" -#include <boost/test/unit_test.hpp> - -#include <irccd/network_stream.hpp> - -using boost::asio::io_service; -using boost::asio::ip::tcp; - -namespace irccd { - -class network_stream_test { -protected: - io_service service_; - tcp::socket connection_{service_}; - ip_network_stream stream_{service_}; - - network_stream_test() - { - // Bind to a random port. - tcp::endpoint ep(tcp::v4(), 0); - tcp::acceptor acceptor(service_, ep); - - acceptor.async_accept(connection_, [] (auto code) { - if (code) - throw boost::system::system_error(code); - }); - stream_.get_socket().async_connect(acceptor.local_endpoint(), [] (auto code) { - if (code) - throw boost::system::system_error(code); - }); - - service_.run(); - service_.reset(); - } -}; - -BOOST_FIXTURE_TEST_SUITE(network_stream_test_suite, network_stream_test) - -BOOST_AUTO_TEST_CASE(multiple_recv) -{ - const auto msg1 = nlohmann::json({{ "abc", 123 }}).dump(0) + "\r\n\r\n"; - const auto msg2 = nlohmann::json({{ "def", 456 }}).dump(0) + "\r\n\r\n"; - - stream_.recv([] (auto code, auto message) { - BOOST_TEST(!code); - BOOST_TEST(message["abc"].template get<int>() == 123); - }); - stream_.recv([] (auto code, auto message) { - BOOST_TEST(!code); - BOOST_TEST(message["def"].template get<int>() == 456); - }); - - boost::asio::async_write(connection_, boost::asio::buffer(msg1), [] (auto code, auto) { - BOOST_TEST(!code); - }); - boost::asio::async_write(connection_, boost::asio::buffer(msg2), [] (auto code, auto) { - BOOST_TEST(!code); - }); - - service_.run(); -} - -BOOST_AUTO_TEST_CASE(multiple_send) -{ - boost::asio::streambuf input; - - stream_.send({{ "abc", 123 }}, [] (auto code) { - BOOST_TEST(!code); - }); - stream_.send({{ "def", 456 }}, [] (auto code) { - BOOST_TEST(!code); - }); - - boost::asio::async_read_until(connection_, input, "\r\n\r\n", [&] (auto code, auto xfer) { - BOOST_TEST(!code); - - const auto json = nlohmann::json::parse(std::string( - boost::asio::buffers_begin(input.data()), - boost::asio::buffers_begin(input.data()) + xfer - 4 - )); - - input.consume(xfer); - - BOOST_TEST(json["abc"].template get<int>() == 123); - }); - - service_.run(); - service_.reset(); - - boost::asio::async_read_until(connection_, input, "\r\n\r\n", [&] (auto code, auto xfer) { - BOOST_TEST(!code); - - const auto json = nlohmann::json::parse(std::string( - boost::asio::buffers_begin(input.data()), - boost::asio::buffers_begin(input.data()) + xfer - 4 - )); - - input.consume(xfer); - - BOOST_TEST(json["def"].template get<int>() == 456); - }); - - service_.run(); -} - -BOOST_AUTO_TEST_CASE(invalid_argument) -{ - const std::string msg1("not a json object\r\n\r\n"); - - stream_.recv([] (auto code, auto message) { - BOOST_TEST(code == boost::system::errc::invalid_argument); - BOOST_TEST(message.is_null()); - }); - - boost::asio::async_write(connection_, boost::asio::buffer(msg1), [] (auto code, auto) { - BOOST_TEST(!code); - }); - - service_.run(); -} - -BOOST_AUTO_TEST_CASE(network_down) -{ - stream_.recv([] (auto code, auto message) { - BOOST_TEST(code == boost::system::errc::network_down); - BOOST_TEST(message.is_null()); - }); - - connection_.close(); - service_.run(); -} - -BOOST_AUTO_TEST_SUITE_END() - -} // !irccd
--- a/tests/src/libirccd/command-plugin-config/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-plugin-config/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(set) { daemon_->plugins().add(std::make_unique<custom_plugin>("test")); - ctl_->send({ + ctl_->write({ { "command", "plugin-config" }, { "plugin", "test" }, { "variable", "verbosy" },
--- a/tests/src/libirccd/command-plugin-load/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-plugin-load/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE(basic) { - ctl_->send({ + ctl_->write({ { "command", "plugin-load" }, { "plugin", "magic" } });
--- a/tests/src/libirccd/command-plugin-reload/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-plugin-reload/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(basic) { - ctl_->send({ + ctl_->write({ { "command", "plugin-reload" }, { "plugin", "test" } });
--- a/tests/src/libirccd/command-plugin-unload/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-plugin-unload/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(basic) { - ctl_->send({ + ctl_->write({ { "command", "plugin-unload" }, { "plugin", "test" } });
--- a/tests/src/libirccd/command-rule-move/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-rule-move/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -70,12 +70,12 @@ { nlohmann::json result; - ctl_->send({ + ctl_->write({ { "command", "rule-move" }, { "from", 2 }, { "to", 0 } }); - ctl_->recv([&] (auto, auto msg) { + ctl_->read([&] (auto, auto msg) { result = msg; }); @@ -86,8 +86,8 @@ BOOST_TEST(result.is_object()); result = nullptr; - ctl_->send({{ "command", "rule-list" }}); - ctl_->recv([&] (auto, auto msg) { + ctl_->write({{ "command", "rule-list" }}); + ctl_->read([&] (auto, auto msg) { result = msg; }); @@ -144,12 +144,12 @@ { nlohmann::json result; - ctl_->send({ + ctl_->write({ { "command", "rule-move" }, { "from", 0 }, { "to", 2 } }); - ctl_->recv([&] (auto, auto msg) { + ctl_->read([&] (auto, auto msg) { result = msg; }); @@ -160,8 +160,8 @@ BOOST_TEST(result.is_object()); result = nullptr; - ctl_->send({{ "command", "rule-list" }}); - ctl_->recv([&] (auto, auto msg) { + ctl_->write({{ "command", "rule-list" }}); + ctl_->read([&] (auto, auto msg) { result = msg; }); @@ -218,12 +218,12 @@ { nlohmann::json result; - ctl_->send({ + ctl_->write({ { "command", "rule-move" }, { "from", 1 }, { "to", 1 } }); - ctl_->recv([&] (auto, auto msg) { + ctl_->read([&] (auto, auto msg) { result = msg; }); @@ -234,8 +234,8 @@ BOOST_TEST(result.is_object()); result = nullptr; - ctl_->send({{ "command", "rule-list" }}); - ctl_->recv([&] (auto, auto msg) { + ctl_->write({{ "command", "rule-list" }}); + ctl_->read([&] (auto, auto msg) { result = msg; }); @@ -292,12 +292,12 @@ { nlohmann::json result; - ctl_->send({ + ctl_->write({ { "command", "rule-move" }, { "from", 0 }, { "to", 123 } }); - ctl_->recv([&] (auto, auto msg) { + ctl_->read([&] (auto, auto msg) { result = msg; }); @@ -308,8 +308,8 @@ BOOST_TEST(result.is_object()); result = nullptr; - ctl_->send({{ "command", "rule-list" }}); - ctl_->recv([&] (auto, auto msg) { + ctl_->write({{ "command", "rule-list" }}); + ctl_->read([&] (auto, auto msg) { result = msg; }); @@ -366,122 +366,67 @@ BOOST_AUTO_TEST_CASE(invalid_index_1_from) { - boost::system::error_code result; - nlohmann::json message; - - ctl_->send({ + const auto result = request({ { "command", "rule-move" }, { "from", -100 }, { "to", 0 } }); - ctl_->recv([&] (auto rresult, auto rmessage) { - result = rresult; - message = rmessage; - }); - wait_for([&] { - return result; - }); - - BOOST_TEST(result == rule_error::invalid_index); - BOOST_TEST(message["error"].template get<int>() == rule_error::invalid_index); - BOOST_TEST(message["errorCategory"].template get<std::string>() == "rule"); + BOOST_TEST(result.second == rule_error::invalid_index); + BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index); + BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule"); } BOOST_AUTO_TEST_CASE(invalid_index_1_to) { - boost::system::error_code result; - nlohmann::json message; - - ctl_->send({ + const auto result = request({ { "command", "rule-move" }, { "from", 0 }, { "to", -100 } }); - ctl_->recv([&] (auto rresult, auto rmessage) { - result = rresult; - message = rmessage; - }); - wait_for([&] { - return result; - }); - - BOOST_TEST(result == rule_error::invalid_index); - BOOST_TEST(message["error"].template get<int>() == rule_error::invalid_index); - BOOST_TEST(message["errorCategory"].template get<std::string>() == "rule"); + BOOST_TEST(result.second == rule_error::invalid_index); + BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index); + BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule"); } BOOST_AUTO_TEST_CASE(invalid_index_2_from) { - boost::system::error_code result; - nlohmann::json message; - - ctl_->send({ + const auto result = request({ { "command", "rule-move" }, { "from", 100 }, { "to", 0 } }); - ctl_->recv([&] (auto rresult, auto rmessage) { - result = rresult; - message = rmessage; - }); - wait_for([&] { - return result; - }); - - BOOST_TEST(result == rule_error::invalid_index); - BOOST_TEST(message["error"].template get<int>() == rule_error::invalid_index); - BOOST_TEST(message["errorCategory"].template get<std::string>() == "rule"); + BOOST_TEST(result.second == rule_error::invalid_index); + BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index); + BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule"); } BOOST_AUTO_TEST_CASE(invalid_index_3_from) { - boost::system::error_code result; - nlohmann::json message; - - ctl_->send({ + const auto result = request({ { "command", "rule-move" }, { "from", "notaint" }, { "to", 0 } }); - ctl_->recv([&] (auto rresult, auto rmessage) { - result = rresult; - message = rmessage; - }); - wait_for([&] { - return result; - }); - - BOOST_TEST(result == rule_error::invalid_index); - BOOST_TEST(message["error"].template get<int>() == rule_error::invalid_index); - BOOST_TEST(message["errorCategory"].template get<std::string>() == "rule"); + BOOST_TEST(result.second == rule_error::invalid_index); + BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index); + BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule"); } BOOST_AUTO_TEST_CASE(invalid_index_3_to) { - boost::system::error_code result; - nlohmann::json message; - - ctl_->send({ + const auto result = request({ { "command", "rule-move" }, { "from", 0 }, { "to", "notaint" } }); - ctl_->recv([&] (auto rresult, auto rmessage) { - result = rresult; - message = rmessage; - }); - wait_for([&] { - return result; - }); - - BOOST_TEST(result == rule_error::invalid_index); - BOOST_TEST(message["error"].template get<int>() == rule_error::invalid_index); - BOOST_TEST(message["errorCategory"].template get<std::string>() == "rule"); + BOOST_TEST(result.second == rule_error::invalid_index); + BOOST_TEST(result.first["error"].template get<int>() == rule_error::invalid_index); + BOOST_TEST(result.first["errorCategory"].template get<std::string>() == "rule"); } BOOST_AUTO_TEST_SUITE_END()
--- a/tests/src/libirccd/command-server-invite/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-server-invite/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(basic) { - ctl_->send({ + ctl_->write({ { "command", "server-invite" }, { "server", "test" }, { "target", "francis" },
--- a/tests/src/libirccd/command-server-join/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-server-join/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(basic) { - ctl_->send({ + ctl_->write({ { "command", "server-join" }, { "server", "test" }, { "channel", "#music" },
--- a/tests/src/libirccd/command-server-kick/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-server-kick/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(basic) { - ctl_->send({ + ctl_->write({ { "command", "server-kick" }, { "server", "test" }, { "target", "francis" }, @@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(noreason) { - ctl_->send({ + ctl_->write({ { "command", "server-kick" }, { "server", "test" }, { "target", "francis" },
--- a/tests/src/libirccd/command-server-mode/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-server-mode/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(basic) { - ctl_->send({ + ctl_->write({ { "command", "server-mode" }, { "server", "test" }, { "channel", "#irccd" },
--- a/tests/src/libirccd/command-server-notice/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-server-notice/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(basic) { - ctl_->send({ + ctl_->write({ { "command", "server-notice" }, { "server", "test" }, { "target", "#staff" },
--- a/tests/src/libirccd/command-server-part/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-server-part/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(basic) { - ctl_->send({ + ctl_->write({ { "command", "server-part" }, { "server", "test" }, { "channel", "#staff" }, @@ -65,7 +65,7 @@ BOOST_AUTO_TEST_CASE(noreason) { - ctl_->send({ + ctl_->write({ { "command", "server-part" }, { "server", "test" }, { "channel", "#staff" }
--- a/tests/src/libirccd/command-server-reconnect/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-server-reconnect/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(basic) { - ctl_->send({ + ctl_->write({ { "command", "server-reconnect" }, { "server", "s1" } }); @@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(all) { - ctl_->send({{"command", "server-reconnect"}}); + ctl_->write({{"command", "server-reconnect"}}); wait_for([this] () { return !server1_->cqueue().empty() && !server2_->cqueue().empty();
--- a/tests/src/libirccd/command-server-topic/main.cpp Fri Apr 06 22:06:07 2018 +0200 +++ b/tests/src/libirccd/command-server-topic/main.cpp Tue Apr 10 21:20:30 2018 +0200 @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(basic) { - ctl_->send({ + ctl_->write({ { "command", "server-topic" }, { "server", "test" }, { "channel", "#staff" },