Mercurial > irccd
changeset 569:24b79bccc181
Irccd: initial support of error code responses
Bring several new types for describing precise errors using:
- irccd_error: for general errors including connecting/recv/send,
- server_error: server and server_service,
- rule_error: rule and rule_service,
- plugin_error: plugin and plugin_service.
No error string are sent to the client anymore.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 28 Nov 2017 13:57:09 +0100 |
parents | ed986ae52656 |
children | 153e84e7b09b |
files | irccdctl/cli.cpp libcommon/CMakeLists.txt libcommon/irccd/network_errc.cpp libcommon/irccd/network_errc.hpp libcommon/irccd/network_stream.hpp libirccd/irccd/basic_transport_client.hpp libirccd/irccd/command.cpp libirccd/irccd/irccd.cpp libirccd/irccd/irccd.hpp libirccd/irccd/plugin.cpp libirccd/irccd/plugin.hpp libirccd/irccd/plugin_service.cpp libirccd/irccd/rule.cpp libirccd/irccd/rule.hpp libirccd/irccd/server.cpp libirccd/irccd/server.hpp libirccd/irccd/transport_client.cpp libirccd/irccd/transport_client.hpp libirccd/irccd/transport_server.cpp libirccd/irccd/transport_service.cpp libirccdctl/CMakeLists.txt libirccdctl/irccd/ctl/controller.cpp |
diffstat | 22 files changed, 576 insertions(+), 333 deletions(-) [+] |
line wrap: on
line diff
--- a/irccdctl/cli.cpp Tue Nov 28 12:20:58 2017 +0100 +++ b/irccdctl/cli.cpp Tue Nov 28 13:57:09 2017 +0100 @@ -19,7 +19,6 @@ #include <boost/system/system_error.hpp> #include <irccd/json_util.hpp> -#include <irccd/network_errc.hpp> #include <irccd/options.hpp> #include <irccd/string_util.hpp> @@ -37,11 +36,6 @@ if (code) throw boost::system::system_error(code); - if (message["error"].is_number_integer()) - throw boost::system::system_error(static_cast<network_errc>(message["error"].template get<int>())); - if (message["error"].is_string()) - throw std::runtime_error(message["error"].template get<std::string>()); - auto c = json_util::to_string(message["command"]); if (c != req["command"].get<std::string>()) {
--- a/libcommon/CMakeLists.txt Tue Nov 28 12:20:58 2017 +0100 +++ b/libcommon/CMakeLists.txt Tue Nov 28 13:57:09 2017 +0100 @@ -26,7 +26,6 @@ ${libcommon_SOURCE_DIR}/irccd/ini.hpp ${libcommon_SOURCE_DIR}/irccd/json_util.hpp ${libcommon_SOURCE_DIR}/irccd/logger.hpp - ${libcommon_SOURCE_DIR}/irccd/network_errc.hpp ${libcommon_SOURCE_DIR}/irccd/network_stream.hpp ${libcommon_SOURCE_DIR}/irccd/options.hpp ${libcommon_SOURCE_DIR}/irccd/string_util.hpp @@ -40,7 +39,6 @@ ${libcommon_SOURCE_DIR}/irccd/ini.cpp ${libcommon_SOURCE_DIR}/irccd/json_util.cpp ${libcommon_SOURCE_DIR}/irccd/logger.cpp - ${libcommon_SOURCE_DIR}/irccd/network_errc.cpp ${libcommon_SOURCE_DIR}/irccd/options.cpp ${libcommon_SOURCE_DIR}/irccd/string_util.cpp ${libcommon_SOURCE_DIR}/irccd/system.cpp
--- a/libcommon/irccd/network_errc.cpp Tue Nov 28 12:20:58 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/* - * network_errc.cpp -- describe some error codes - * - * Copyright (c) 2013-2017 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 "network_errc.hpp" - -namespace irccd { - -const boost::system::error_category& network_category() noexcept -{ - static const class network_category : public boost::system::error_category { - public: - const char* name() const noexcept override - { - return "network_category"; - } - - std::string message(int code) const override - { - switch (static_cast<network_errc>(code)) { - case network_errc::no_error: - return "no error"; - case network_errc::invalid_program: - return "invalid program"; - case network_errc::invalid_version: - return "invalid version"; - case network_errc::invalid_auth: - return "invalid authentication"; - case network_errc::invalid_message: - return "invalid message"; - case network_errc::corrupt_message: - return "corrupt message"; - case network_errc::auth_required: - return "auth required"; - default: - return "unknown error"; - } - } - } category; - - return category; -} - -boost::system::error_code make_error_code(network_errc errc) noexcept -{ - return {static_cast<int>(errc), network_category()}; -} - -} // !irccd
--- a/libcommon/irccd/network_errc.hpp Tue Nov 28 12:20:58 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/* - * network_errc.hpp -- describe some error codes - * - * Copyright (c) 2013-2017 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_ERRC_HPP -#define IRCCD_COMMON_NETWORK_ERRC_HPP - -/** - * \file network_errc.hpp - * \brief Describe some error codes. - */ - -#include <boost/system/error_code.hpp> - -#include <type_traits> - -namespace irccd { - -/** - * \brief Error code for transport/irccdctl - */ -enum class network_errc { - no_error = 0, //!< no error (default) - invalid_program, //!< connected daemon is not irccd - invalid_version, //!< irccd daemon is incompatible - invalid_auth, //!< invalid credentials in auth command - invalid_message, //!< the message was not JSON - corrupt_message, //!< error occured while sending a message - auth_required //!< authentication is required and was not issued -}; - -/** - * Get the network category singleton. - * - * \return the category for network_errc enum - */ -const boost::system::error_category& network_category() noexcept; - -/** - * Construct an error_code from network_errc enum. - * - * \return the error code - */ -boost::system::error_code make_error_code(network_errc errc) noexcept; - -} // !irccd - -namespace boost { - -namespace system { - -template <> -struct is_error_code_enum<irccd::network_errc> : public std::true_type { -}; - -} // !system - -} // !boost - -#endif // !IRCCD_COMMON_NETWORK_ERRC_HPP
--- a/libcommon/irccd/network_stream.hpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libcommon/irccd/network_stream.hpp Tue Nov 28 13:57:09 2017 +0100 @@ -39,8 +39,6 @@ #include <json.hpp> -#include "network_errc.hpp" - namespace irccd { /** @@ -167,10 +165,8 @@ 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) { - if (code) - handler(std::move(code), nullptr); - else if (xfer == 0U) - handler(network_errc::corrupt_message, nullptr); + if (code || xfer == 0U) + handler(make_error_code(boost::system::errc::network_down), nullptr); else { std::string str( boost::asio::buffers_begin(rbuffer_.data()), @@ -188,9 +184,9 @@ } catch (...) {} if (!message.is_object()) - handler(network_errc::invalid_message, nullptr); + handler(make_error_code(boost::system::errc::invalid_argument), nullptr); else - handler(network_errc::no_error, std::move(message)); + handler(code, std::move(message)); } }); } @@ -199,12 +195,10 @@ 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) { - if (code) - handler(std::move(code)); - else if (xfer == 0U) - handler(network_errc::corrupt_message); + if (code || xfer == 0U) + handler(make_error_code(boost::system::errc::network_down)); else - handler(network_errc::no_error); + handler(code); }); }
--- a/libirccd/irccd/basic_transport_client.hpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/basic_transport_client.hpp Tue Nov 28 13:57:09 2017 +0100 @@ -74,7 +74,13 @@ */ void do_recv(network_recv_handler handler) override { - stream_.recv(std::move(handler)); + assert(handler); + + auto self = shared_from_this(); + + stream_.recv([this, self, handler] (auto msg, auto code) { + handler(std::move(msg), std::move(code)); + }); } /** @@ -82,7 +88,12 @@ */ void do_send(nlohmann::json json, network_send_handler handler) override { - stream_.send(std::move(json), std::move(handler)); + auto self = shared_from_this(); + + stream_.send(std::move(json), [this, self, handler] (auto code) { + if (handler) + handler(std::move(code)); + }); } };
--- a/libirccd/irccd/command.cpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/command.cpp Tue Nov 28 13:57:09 2017 +0100 @@ -39,16 +39,15 @@ auto value = args.find("value"); if (var == args.end() || !var->is_string()) - client.error("plugin-config", "missing 'variable' property (string expected)"); - else if (!value->is_string()) - client.error("plugin-config", "invalid 'value' property (string expected)"); - else { - auto config = plugin.config(); + throw irccd_error(irccd_error::error::incomplete_message); + if (value == args.end() || !value->is_string()) + throw irccd_error(irccd_error::error::incomplete_message); - config[*var] = *value; - plugin.set_config(config); - client.success("plugin-config"); - } + auto config = plugin.config(); + + config[*var] = *value; + plugin.set_config(config); + client.success("plugin-config"); } void exec_get(transport_client& client, plugin& plugin, const nlohmann::json& args) @@ -262,11 +261,10 @@ auto server = server::from_json(irccd.service(), args); if (irccd.servers().has(server->name())) - client.error("server-connect", "server already exists"); - else { - irccd.servers().add(std::move(server)); - client.success("server-connect"); - } + throw server_error(server_error::error::already_exists); + + irccd.servers().add(std::move(server)); + client.success("server-connect"); } server_disconnect_command::server_disconnect_command() @@ -280,8 +278,14 @@ if (it == args.end()) irccd.servers().clear(); - else - irccd.servers().remove(*it); + else { + if (!it->is_string()) + throw server_error(server_error::error::invalid_identifier); + if (!irccd.servers().has(it->get<std::string>())) + throw server_error(server_error::error::not_found); + + irccd.servers().remove(it->get<std::string>()); + } client.success("server-disconnect"); } @@ -523,19 +527,15 @@ auto action = args.find("action"); if (action != args.end()) { - if (!action->is_string()) { - client.error("rule-edit", "action must be \"accept\" or \"drop\""); - return; - } + if (!action->is_string()) + throw rule_error(rule_error::error::invalid_action); if (action->get<std::string>() == "accept") rule.set_action(rule::action_type::accept); else if (action->get<std::string>() == "drop") rule.set_action(rule::action_type::drop); - else { - client.error("rule-edit", "invalid action '"s + action->get<std::string>() + "'"); - return; - } + else + throw rule_error(rule_error::error::invalid_action); } // All done, sync the rule. @@ -583,14 +583,11 @@ { unsigned position = json_util::require_uint(args, "index"); - if (irccd.rules().length() == 0) - client.error("rule-remove", "rule list is empty"); - if (position >= irccd.rules().length()) - client.error("rule-remove", "index is out of range"); - else { - irccd.rules().remove(position); - client.success("rule-remove"); - } + if (irccd.rules().length() == 0 || position >= irccd.rules().length()) + throw rule_error(rule_error::error::invalid_index); + + irccd.rules().remove(position); + client.success("rule-remove"); } rule_move_command::rule_move_command() @@ -633,18 +630,20 @@ * After: [1] [2] [0] */ - // Ignore dump input. - if (from == to) + // Ignore dumb input. + if (from == to) { client.success("rule-move"); - else if (from >= irccd.rules().length()) - client.error("rule-move", "rule source index is out of range"); - else { - auto save = irccd.rules().list()[from]; + return; + } + + if (from >= irccd.rules().length()) + throw rule_error(rule_error::error::invalid_index); - irccd.rules().remove(from); - irccd.rules().insert(save, to > irccd.rules().length() ? irccd.rules().length() : to); - client.success("rule-move"); - } + auto save = irccd.rules().list()[from]; + + irccd.rules().remove(from); + irccd.rules().insert(save, to > irccd.rules().length() ? irccd.rules().length() : to); + client.success("rule-move"); } rule_add_command::rule_add_command() @@ -658,11 +657,10 @@ auto rule = from_json(args); if (index > irccd.rules().length()) - client.error("rule-add", "index is out of range"); - else { - irccd.rules().insert(rule, index); - client.success("rule-add"); - } + throw rule_error(rule_error::error::invalid_index); + + irccd.rules().insert(rule, index); + client.success("rule-add"); } } // !irccd
--- a/libirccd/irccd/irccd.cpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/irccd.cpp Tue Nov 28 13:57:09 2017 +0100 @@ -38,4 +38,44 @@ irccd::~irccd() = default; +const boost::system::error_category& irccd_category() +{ + static const class category : public boost::system::error_category { + public: + const char* name() const noexcept override + { + return "irccd"; + } + + std::string message(int e) const override + { + switch (static_cast<irccd_error::error>(e)) { + case irccd_error::error::not_irccd: + return "daemon is not irccd instance"; + case irccd_error::error::incompatible_version: + return "major version is incompatible"; + case irccd_error::error::auth_required: + return "authentication is required"; + case irccd_error::error::invalid_auth: + return "invalid authentication"; + case irccd_error::error::invalid_message: + return "invalid message"; + case irccd_error::error::invalid_command: + return "invalid command"; + case irccd_error::error::incomplete_message: + return "command requires more arguments"; + default: + return "no error"; + } + } + } category; + + return category; +} + +boost::system::error_code make_error_code(irccd_error::error e) +{ + return {static_cast<int>(e), irccd_category()}; +} + } // !irccd
--- a/libirccd/irccd/irccd.hpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/irccd.hpp Tue Nov 28 13:57:09 2017 +0100 @@ -173,6 +173,72 @@ } }; +/** + * \brief Irccd error. + */ +class irccd_error : public boost::system::system_error { +public: + /** + * \brief Irccd related errors (1..999) + */ + enum error { + //!< No error. + no_error = 0, + + //!< The connected peer is not irccd. + not_irccd, + + //!< The irccd version is too different. + incompatible_version, + + //!< Authentication was required but not issued. + auth_required, + + //!< Authentication was invalid. + invalid_auth, + + //!< The message was not a valid JSON object. + invalid_message, + + //!< The specified command does not exist. + invalid_command, + + //!< The specified command requires more arguments. + incomplete_message, + }; + + /** + * Inherited constructors. + */ + using system_error::system_error; +}; + +/** + * Get the irccd error category singleton. + * + * \return the singleton + */ +const boost::system::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); + } // !irccd +namespace boost { + +namespace system { + +template <> +struct is_error_code_enum<irccd::irccd_error::error> : public std::true_type { +}; + +} // !system + +} // !boost + #endif // !IRCCD_HPP
--- a/libirccd/irccd/plugin.cpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/plugin.cpp Tue Nov 28 13:57:09 2017 +0100 @@ -68,4 +68,36 @@ return plugin; } +const boost::system::error_category& plugin_category() +{ + static const class category : public boost::system::error_category { + public: + const char* name() const noexcept override + { + return "plugin"; + } + + std::string message(int e) const override + { + switch (static_cast<plugin_error::error>(e)) { + case plugin_error::not_found: + return "plugin not found"; + case plugin_error::exec_error: + return "plugin exec error"; + case plugin_error::already_exists: + return "plugin already exists"; + default: + return "no error"; + } + } + } category; + + return category; +} + +boost::system::error_code make_error_code(plugin_error::error e) +{ + return {static_cast<int>(e), plugin_category()}; +} + } // !irccd
--- a/libirccd/irccd/plugin.hpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/plugin.hpp Tue Nov 28 13:57:09 2017 +0100 @@ -559,6 +559,60 @@ virtual std::shared_ptr<plugin> find(const std::string& id) noexcept; }; +/** + * \brief Plugin error. + */ +class plugin_error : public boost::system::system_error { +public: + /** + * \brief Server related errors (3000..3999) + */ + enum error { + //!< No error. + no_error = 0, + + //!< The specified plugin is not found. + not_found = 2000, + + //!< The plugin was unable to run the function. + exec_error, + + //!< The plugin is already loaded. + already_exists, + }; + + /** + * Inherited constructors. + */ + using system_error::system_error; +}; + +/** + * Get the plugin error category singleton. + * + * \return the singleton + */ +const boost::system::error_category& server_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); + } // !irccd +namespace boost { + +namespace system { + +template <> +struct is_error_code_enum<irccd::plugin_error::error> : public std::true_type { +}; + +} // !system + +} // !boost + #endif // !IRCCD_PLUGIN_HPP
--- a/libirccd/irccd/plugin_service.cpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/plugin_service.cpp Tue Nov 28 13:57:09 2017 +0100 @@ -75,7 +75,7 @@ auto plugin = get(name); if (!plugin) - throw std::invalid_argument(string_util::sprintf("plugin %s not found", name)); + throw plugin_error(plugin_error::not_found); return plugin; } @@ -154,25 +154,22 @@ if (has(name)) return; - try { - std::shared_ptr<plugin> plugin; + std::shared_ptr<plugin> plugin; - if (path.empty()) - plugin = find(name); - else - plugin = open(name, std::move(path)); + if (path.empty()) + plugin = find(name); + else + plugin = open(name, std::move(path)); - if (plugin) { - plugin->set_config(config(name)); - plugin->set_formats(formats(name)); - plugin->set_paths(paths(name)); - plugin->on_load(irccd_); + if (!plugin) + throw plugin_error(plugin_error::not_found); - add(std::move(plugin)); - } - } catch (const std::exception& ex) { - log::warning(string_util::sprintf("plugin %s: %s", name, ex.what())); - } + plugin->set_config(config(name)); + plugin->set_formats(formats(name)); + plugin->set_paths(paths(name)); + plugin->on_load(irccd_); + + add(std::move(plugin)); } void plugin_service::reload(const std::string& name)
--- a/libirccd/irccd/rule.cpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/rule.cpp Tue Nov 28 13:57:09 2017 +0100 @@ -56,4 +56,32 @@ match_set(events_, event); } +const boost::system::error_category& rule_category() +{ + static const class category : public boost::system::error_category { + public: + const char* name() const noexcept override + { + return "rule"; + } + + std::string message(int e) const override + { + switch (static_cast<rule_error::error>(e)) { + case rule_error::error::invalid_action: + return "invalid action given"; + default: + return "no error"; + } + } + } category; + + return category; +} + +boost::system::error_code make_error_code(rule_error::error e) +{ + return {static_cast<int>(e), rule_category()}; +} + } // !irccd
--- a/libirccd/irccd/rule.hpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/rule.hpp Tue Nov 28 13:57:09 2017 +0100 @@ -24,11 +24,13 @@ * \brief Rule description */ +#include "sysconfig.hpp" + #include <cassert> #include <string> #include <unordered_set> -#include "sysconfig.hpp" +#include <boost/system/system_error.hpp> namespace irccd { @@ -212,6 +214,57 @@ } }; +/** + * \brief Rule error. + */ +class rule_error : public boost::system::system_error { +public: + /** + * \brief Rule related errors (4000..4999) + */ + enum class error { + //!< No error. + no_error = 0, + + //!< Invalid action given. + invalid_action = 4000, + + //!< Invalid rule index. + invalid_index, + }; + + /** + * Inherited constructors. + */ + using system_error::system_error; +}; + +/** + * Get the rule error category singleton. + * + * \return the singleton + */ +const boost::system::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); + } // !irccd +namespace boost { + +namespace system { + +template <> +struct is_error_code_enum<irccd::rule_error::error> : public std::true_type { +}; + +} // !system + +} // !boost + #endif // !IRCCD_RULE_HPP
--- a/libirccd/irccd/server.cpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/server.cpp Tue Nov 28 13:57:09 2017 +0100 @@ -707,4 +707,48 @@ send(string_util::sprintf("WHOIS %s %s", target, target)); } +const boost::system::error_category& server_category() +{ + static const class category : public boost::system::error_category { + public: + const char* name() const noexcept override + { + return "server"; + } + + std::string message(int e) const override + { + switch (static_cast<server_error::error>(e)) { + case server_error::not_found: + return "server not found"; + case server_error::error::invalid_identifier: + return "invalid identifier"; + case server_error::error::not_connected: + return "server is not connected"; + case server_error::error::already_connected: + return "server is already connected"; + case server_error::error::invalid_port_number: + return "invalid port number specified"; + case server_error::error::invalid_reconnect_tries_number: + return "invalid number of reconnection tries"; + case server_error::error::invalid_reconnect_timeout_number: + return "invalid reconnect timeout number"; + case server_error::error::invalid_host: + return "invalid hostname"; + case server_error::error::ssl_disabled: + return "ssl is not enabled"; + default: + return "no error"; + } + } + } category; + + return category; +} + +boost::system::error_code make_error_code(server_error::error e) +{ + return {static_cast<int>(e), server_category()}; +} + } // !irccd
--- a/libirccd/irccd/server.hpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/server.hpp Tue Nov 28 13:57:09 2017 +0100 @@ -932,6 +932,81 @@ virtual void whois(std::string target); }; +/** + * \brief Server error. + */ +class server_error : public boost::system::system_error { +public: + /** + * \brief Server related errors (1000..1999) + */ + enum error { + //!< No error. + no_error = 0, + + //!< The specified server was not found. + not_found = 1000, + + //!< The specified identifier is invalid. + invalid_identifier, + + //!< The server is not connected. + not_connected, + + //!< The server is already connected. + already_connected, + + //!< Server with same name already exists. + already_exists, + + //!< The specified port number is invalid. + invalid_port_number, + + //!< The specified reconnect tries number is invalid. + invalid_reconnect_tries_number, + + //!< The specified reconnect reconnect number is invalid. + invalid_reconnect_timeout_number, + + //!< The specified host was invalid. + invalid_host, + + //!< SSL was requested but is disabled. + ssl_disabled, + }; + + /** + * Inherited constructors. + */ + using system_error::system_error; +}; + +/** + * Get the server error category singleton. + * + * \return the singleton + */ +const boost::system::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); + } // !irccd +namespace boost { + +namespace system { + +template <> +struct is_error_code_enum<irccd::server_error::error> : public std::true_type { +}; + +} // !system + +} // !boost + #endif // !IRCCD_SERVER_HPP
--- a/libirccd/irccd/transport_client.cpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/transport_client.cpp Tue Nov 28 13:57:09 2017 +0100 @@ -23,12 +23,6 @@ namespace irccd { -void transport_client::close() -{ - state_ = state_t::closing; - parent_.clients().erase(shared_from_this()); -} - void transport_client::recv(network_recv_handler handler) { if (state_ != state_t::closing) @@ -48,45 +42,32 @@ send({{ "command", cname }}, std::move(handler)); } -void transport_client::error(const nlohmann::json& data, network_send_handler handler) -{ - send(std::move(data), std::move(handler)); - set_state(state_t::closing); -} - -void transport_client::error(const std::string& cname, const std::string& reason, network_send_handler handler) +void transport_client::error(boost::system::error_code code, network_send_handler handler) { - assert(!cname.empty()); - assert(!reason.empty()); - - error({ - { "command", cname }, - { "error", reason } - }, std::move(handler)); + error(std::move(code), "", std::move(handler)); } -void transport_client::error(const std::string& reason, network_send_handler handler) +void transport_client::error(boost::system::error_code code, + std::string cname, + network_send_handler handler) { - assert(!reason.empty()); + assert(code); - error({{ "error", reason }}, std::move(handler)); -} - -void transport_client::error(const std::string& cname, network_errc reason, network_send_handler handler) -{ - assert(!cname.empty()); + auto json = nlohmann::json::object({ + { "error", code.value() } + }); - error({ - { "command", cname }, - { "error", static_cast<int>(reason) } - }, std::move(handler)); -} + if (!cname.empty()) + json["command"] = std::move(cname); -void transport_client::error(network_errc reason, network_send_handler handler) -{ - assert(reason != network_errc::no_error); + send(std::move(json), [this, handler] (auto code) { + if (handler) + handler(code); - error({{ "error", static_cast<int>(reason) }}, std::move(handler)); + parent_.clients().erase(shared_from_this()); + }); + + state_ = state_t::closing; } } // !irccd
--- a/libirccd/irccd/transport_client.hpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/transport_client.hpp Tue Nov 28 13:57:09 2017 +0100 @@ -45,8 +45,6 @@ state_t state_{state_t::authenticating}; transport_server& parent_; - void close(); - protected: /** * Request a receive operation. @@ -81,6 +79,11 @@ } /** + * Virtual destructor defaulted. + */ + virtual ~transport_client() = default; + + /** * Get the transport server parent. * * \return the parent @@ -123,6 +126,11 @@ /** * Start receiving if not closed. * + * Possible error codes: + * + * - boost::system::errc::network_down in case of errors, + * - boost::system::errc::invalid_argument if the JSON message is invalid. + * * \pre handler != nullptr * \param handler the handler */ @@ -131,6 +139,10 @@ /** * Start sending if not closed. * + * Possible error codes: + * + * - boost::system::errc::network_down in case of errors, + * * \param json the json message * \param handler the optional handler */ @@ -145,58 +157,24 @@ void success(const std::string& cname, network_send_handler handler = nullptr); /** - * Send a error message, the state is set to closing. - * - * The invocation is similar to: - * - * ````cpp - * set_state(state_t::closing); - * send(message, handler); - * ```` + * Send an error code to the client. * - * \pre message is not null - * \pre data.is_object() - * \param message the error message - * \param handler the handler - */ - void error(const nlohmann::json& data, network_send_handler handler = nullptr); - - /** - * Convenient error overload. - * - * \param cname the command name - * \pre !reason.empty() - * \param reason the reason string + * \pre code is not 0 + * \param code the error code * \param handler the optional handler */ - void error(const std::string& cname, const std::string& reason, network_send_handler handler = nullptr); - - /** - * Convenient error overload. - * - * \pre !reason.empty() - * \param reason the reason string - * \param handler the handler - */ - void error(const std::string& reason, network_send_handler handler = nullptr); + void error(boost::system::error_code code, network_send_handler handler = nullptr); /** - * Convenient error overload. + * Send an error code to the client. * - * \param cname the command name - * \param reason the error code + * \pre code is not 0 + * \param code the error code * \param handler the optional handler */ - void error(const std::string& cname, network_errc reason, network_send_handler handler = nullptr); - - /** - * Convenient error overload. - * - * \pre reason != network_errc::no_error - * \param reason the reason string - * \param handler the handler - */ - void error(network_errc reason, network_send_handler handler = nullptr); + void error(boost::system::error_code code, + std::string cname, + network_send_handler handler = nullptr); }; } // !irccd
--- a/libirccd/irccd/transport_server.cpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/transport_server.cpp Tue Nov 28 13:57:09 2017 +0100 @@ -20,6 +20,7 @@ #include <cassert> +#include "irccd.hpp" #include "json_util.hpp" #include "transport_server.hpp" @@ -40,15 +41,15 @@ auto password = json_util::to_string(message["password"]); if (command != "auth") { - client->error(network_errc::auth_required); - code = network_errc::auth_required; + client->error(irccd_error::auth_required); + code = irccd_error::auth_required; } else if (password != password_) { - client->error(network_errc::invalid_auth); - code = network_errc::invalid_auth; + client->error(irccd_error::invalid_auth); + code = irccd_error::invalid_auth; } else { client->set_state(transport_client::state_t::ready); client->success("auth"); - code = network_errc::no_error; + code = irccd_error::no_error; } handler(std::move(code), std::move(client)); @@ -78,8 +79,10 @@ handler(std::move(code), std::move(client)); else if (!password_.empty()) do_auth(std::move(client), std::move(handler)); - else + else { + client->set_state(transport_client::state_t::ready); handler(std::move(code), std::move(client)); + } }); }
--- a/libirccd/irccd/transport_service.cpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccd/irccd/transport_service.cpp Tue Nov 28 13:57:09 2017 +0100 @@ -32,18 +32,24 @@ assert(object.is_object()); auto name = object.find("command"); - if (name == object.end() || !name->is_string()) + + if (name == object.end() || !name->is_string()) { + tc->error(irccd_error::invalid_message); return; + } auto cmd = irccd_.commands().find(*name); if (!cmd) - tc->error(*name, "command does not exist"); + tc->error(irccd_error::invalid_command, name->get<std::string>()); else { try { cmd->exec(irccd_, *tc, object); + } catch (const boost::system::system_error& ex) { + tc->error(ex.code(), cmd->name()); } catch (const std::exception& ex) { - tc->error(cmd->name(), ex.what()); + log::warning() << "transport: unknown error not reported" << std::endl; + log::warning() << "transport: " << ex.what() << std::endl; } } } @@ -51,11 +57,20 @@ void transport_service::do_recv(std::shared_ptr<transport_client> tc) { tc->recv([this, tc] (auto code, auto json) { - if (code) - log::warning() << "transport: " << code.message() << std::endl; - else { - do_recv(tc); - handle_command(std::move(tc), json); + switch (code.value()) { + case boost::system::errc::network_down: + log::warning("transport: client disconnected"); + break; + case boost::system::errc::invalid_argument: + tc->error(irccd_error::invalid_message); + break; + default: + handle_command(tc, json); + + if (tc->state() == transport_client::state_t::ready) + do_recv(std::move(tc)); + + break; } }); } @@ -64,7 +79,7 @@ { ts.accept([this, &ts] (auto code, auto client) { if (code) - log::warning() << "transport: " << code.message() << std::endl; + log::warning() << "transport: new client error: " << code.message() << std::endl; else { do_accept(ts); do_recv(std::move(client));
--- a/libirccdctl/CMakeLists.txt Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccdctl/CMakeLists.txt Tue Nov 28 13:57:09 2017 +0100 @@ -47,6 +47,7 @@ LIBRARIES ${LIBRARIES} libcommon + libirccd PUBLIC_INCLUDES $<BUILD_INTERFACE:${libirccdctl_SOURCE_DIR}> )
--- a/libirccdctl/irccd/ctl/controller.cpp Tue Nov 28 12:20:58 2017 +0100 +++ b/libirccdctl/irccd/ctl/controller.cpp Tue Nov 28 13:57:09 2017 +0100 @@ -18,10 +18,14 @@ #include <cassert> -#include <irccd/network_errc.hpp> #include <irccd/sysconfig.hpp> #include <irccd/json_util.hpp> +#include <irccd/irccd.hpp> +#include <irccd/server.hpp> +#include <irccd/plugin.hpp> +#include <irccd/rule.hpp> + #include "controller.hpp" #include "connection.hpp" @@ -42,12 +46,7 @@ return; } - recv([handler, info, this] (auto code, auto message) { - if (message["error"].is_number_integer()) - code = static_cast<network_errc>(message["error"].template get<int>()); - if (message["error"].is_string()) - code = network_errc::invalid_auth; - + recv([handler, info] (auto code, auto) { handler(std::move(code), std::move(info)); }); }); @@ -62,9 +61,9 @@ } if (json_util::to_string(message["program"]) != "irccd") - handler(network_errc::invalid_program, std::move(message)); + handler(irccd_error::not_irccd, std::move(message)); else if (json_util::to_int(message["major"]) != IRCCD_VERSION_MAJOR) - handler(network_errc::invalid_version, std::move(message)); + handler(irccd_error::incompatible_version, std::move(message)); else { if (!password_.empty()) authenticate(std::move(handler), message); @@ -92,7 +91,26 @@ // TODO: ensure connected. - conn_.recv(std::move(handler)); + conn_.recv([handler] (auto code, auto msg) { + if (code) { + handler(std::move(code), std::move(msg)); + return; + } + + auto e = json_util::to_int(msg["error"]); + + // TODO: maybe better to pass category instead of using static ranges. + if (e > 0 && e < 1000) + code = make_error_code(static_cast<irccd_error::error>(e)); + else if (e >= 1000 && e < 2000) + code = make_error_code(static_cast<server_error::error>(e)); + else if (e >= 2000 && e < 3000) + code = make_error_code(static_cast<plugin_error::error>(e)); + else if (e >= 4000 && e < 4000) + code = make_error_code(static_cast<rule_error::error>(e)); + + handler(std::move(code), std::move(msg)); + }); } void controller::send(nlohmann::json message, network_send_handler handler)