view libirccd/irccd/daemon/service/transport_service.cpp @ 725:0dbe1842a7d8

Irccd: rework loggers, closes #793 @3h Logger now supports category/component metadata.
author David Demelier <markand@malikania.fr>
date Tue, 17 Jul 2018 22:30:00 +0200
parents ad1ee47165fa
children
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 {

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

    const json_util::document doc(object);
    const auto name = doc.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, doc);
        } 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: "
                << 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" << std::endl;
            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::shared_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