view libirccd/irccd/daemon/service/transport_service.cpp @ 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.
author David Demelier <markand@malikania.fr>
date Tue, 10 Apr 2018 21:20:30 +0200
parents 80640d2c01c5
children 84c0b723100d
line wrap: on
line source

/*
 * transport_service.cpp -- transport service
 *
 * 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 <irccd/sysconfig.hpp>

#include <cassert>

#include <irccd/json_util.hpp>

#include <irccd/daemon/command.hpp>
#include <irccd/daemon/irccd.hpp>
#include <irccd/daemon/logger.hpp>
#include <irccd/daemon/transport_util.hpp>
#include <irccd/daemon/transport_client.hpp>

#include "transport_service.hpp"

namespace irccd {

namespace {

} // !namespace

void transport_service::handle_command(std::shared_ptr<transport_client> tc, const nlohmann::json& object)
{
    assert(object.is_object());

    const auto name = json_util::parser(object).get<std::string>("command");

    if (!name) {
        tc->error(irccd_error::invalid_message);
        return;
    }

    const auto cmd = std::find_if(commands_.begin(), commands_.end(), [&] (const auto& cptr) {
        return cptr->get_name() == *name;
    });

    if (cmd == commands_.end())
        tc->error(irccd_error::invalid_command, *name);
    else {
        try {
            (*cmd)->exec(irccd_, *tc, object);
        } 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;
            irccd_.get_log().warning() << "transport: " << ex.what() << std::endl;
        }
    }
}

void transport_service::do_recv(std::shared_ptr<transport_client> tc)
{
    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 std::errc::invalid_argument:
            tc->error(irccd_error::invalid_message);
            break;
        default:
            // 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));
            }

            break;
        }
    });
}

void transport_service::do_accept(transport_server& ts)
{
    ts.accept([this, &ts] (auto code, auto client) {
        if (!code) {
            do_accept(ts);
            do_recv(std::move(client));

            irccd_.get_log().info() << "transport: new client connected" << std::endl;
        }
    });
}

transport_service::transport_service(irccd& irccd) noexcept
    : irccd_(irccd)
{
}

transport_service::~transport_service() noexcept = default;

void transport_service::add(std::unique_ptr<transport_server> ts)
{
    assert(ts);

    do_accept(*ts);
    servers_.push_back(std::move(ts));
}

void transport_service::broadcast(const nlohmann::json& json)
{
    assert(json.is_object());

    for (const auto& servers : servers_)
        for (const auto& client : servers->get_clients())
            client->write(json);
}

void transport_service::load(const config& cfg) noexcept
{
    for (const auto& section : cfg) {
        if (section.key() != "transport")
            continue;

        try {
            add(transport_util::from_config(irccd_.get_service(), section));
        } catch (const std::exception& ex) {
            irccd_.get_log().warning() << "transport: " << ex.what() << std::endl;
        }
    }
}

} // !irccd