changeset 126:49572a69c41d

Irccd: implement TransportService, #496
author David Demelier <markand@malikania.fr>
date Wed, 11 May 2016 13:00:40 +0200
parents ef4bef0bf0f8
children 77f950caab35
files irccd/main.cpp lib/irccd/CMakeSources.cmake lib/irccd/cmd-server-cmode.cpp lib/irccd/cmd-server-cnotice.cpp lib/irccd/cmd-server-disconnect.cpp lib/irccd/cmd-server-info.cpp lib/irccd/cmd-server-invite.cpp lib/irccd/cmd-server-join.cpp lib/irccd/cmd-server-kick.cpp lib/irccd/cmd-server-list.cpp lib/irccd/cmd-server-me.cpp lib/irccd/cmd-server-message.cpp lib/irccd/cmd-server-mode.cpp lib/irccd/cmd-server-nick.cpp lib/irccd/cmd-server-notice.cpp lib/irccd/cmd-server-part.cpp lib/irccd/cmd-server-reconnect.cpp lib/irccd/cmd-server-topic.cpp lib/irccd/config.cpp lib/irccd/irccd.cpp lib/irccd/irccd.hpp lib/irccd/service-server.cpp lib/irccd/service-transport.cpp lib/irccd/service-transport.hpp
diffstat 24 files changed, 337 insertions(+), 243 deletions(-) [+]
line wrap: on
line diff
--- a/irccd/main.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/irccd/main.cpp	Wed May 11 13:00:40 2016 +0200
@@ -38,6 +38,7 @@
 #include <irccd/options.hpp>
 #include <irccd/path.hpp>
 #include <irccd/service-server.hpp>
+#include <irccd/service-transport.hpp>
 #include <irccd/system.hpp>
 #include <irccd/config.hpp>
 #include <irccd/irccd.hpp>
@@ -228,7 +229,7 @@
 
 	// [transport]
 	for (const auto &transport : config.loadTransports()) {
-		instance->addTransport(transport);
+		instance->transportService().add(transport);
 	}
 
 	// [server] section.
--- a/lib/irccd/CMakeSources.cmake	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/CMakeSources.cmake	Wed May 11 13:00:40 2016 +0200
@@ -65,6 +65,7 @@
 	${CMAKE_CURRENT_LIST_DIR}/service.hpp
 	${CMAKE_CURRENT_LIST_DIR}/service-interrupt.hpp
 	${CMAKE_CURRENT_LIST_DIR}/service-server.hpp
+	${CMAKE_CURRENT_LIST_DIR}/service-transport.hpp
 	${CMAKE_CURRENT_LIST_DIR}/sockets.hpp
 	${CMAKE_CURRENT_LIST_DIR}/system.hpp
 	${CMAKE_CURRENT_LIST_DIR}/timer.hpp
@@ -134,6 +135,7 @@
 	${CMAKE_CURRENT_LIST_DIR}/server-state-disconnected.cpp
 	${CMAKE_CURRENT_LIST_DIR}/service-interrupt.cpp
 	${CMAKE_CURRENT_LIST_DIR}/service-server.cpp
+	${CMAKE_CURRENT_LIST_DIR}/service-transport.cpp
 	${CMAKE_CURRENT_LIST_DIR}/sockets.cpp
 	${CMAKE_CURRENT_LIST_DIR}/system.cpp
 	${CMAKE_CURRENT_LIST_DIR}/timer.cpp
--- a/lib/irccd/cmd-server-cmode.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-cmode.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-cmode.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-cnotice.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-cnotice.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-cnotice.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-disconnect.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-disconnect.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-disconnect.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-info.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-info.cpp	Wed May 11 13:00:40 2016 +0200
@@ -20,6 +20,7 @@
 
 #include "cmd-server-info.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-invite.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-invite.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-invite.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-join.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-join.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-join.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-kick.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-kick.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-kick.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-list.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-list.cpp	Wed May 11 13:00:40 2016 +0200
@@ -20,6 +20,7 @@
 
 #include "cmd-server-list.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-me.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-me.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-me.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-message.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-message.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-message.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-mode.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-mode.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-mode.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-nick.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-nick.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-nick.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-notice.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-notice.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-notice.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-part.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-part.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-part.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-reconnect.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-reconnect.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-reconnect.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/cmd-server-topic.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/cmd-server-topic.cpp	Wed May 11 13:00:40 2016 +0200
@@ -18,6 +18,7 @@
 
 #include "cmd-server-topic.hpp"
 #include "irccd.hpp"
+#include "server.hpp"
 #include "service-server.hpp"
 
 namespace irccd {
--- a/lib/irccd/config.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/config.cpp	Wed May 11 13:00:40 2016 +0200
@@ -27,6 +27,7 @@
 #include "rule.hpp"
 #include "server.hpp"
 #include "sysconfig.hpp"
+#include "transport-server.hpp"
 #include "util.hpp"
 
 using namespace fmt::literals;
--- a/lib/irccd/irccd.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/irccd.cpp	Wed May 11 13:00:40 2016 +0200
@@ -27,6 +27,7 @@
 #include "path.hpp"
 #include "service-interrupt.hpp"
 #include "service-server.hpp"
+#include "service-transport.hpp"
 #include "util.hpp"
 
 using namespace std;
@@ -37,138 +38,14 @@
 
 namespace irccd {
 
-void Irccd::handleTransportCommand(std::weak_ptr<TransportClient> ptr, const json::Value &object)
-{
-	assert(object.isObject());
-
-	post([=] (Irccd &) {
-		/* 0. Be sure the object still exists */
-		auto tc = ptr.lock();
-
-		if (!tc) {
-			return;
-		}
-
-		/* 1. Check if the Json object is valid */
-		auto name = object.find("command");
-		if (name == object.end() || name->typeOf() != json::Type::String) {
-			// TODO: send error
-			log::warning("invalid command object");
-			return;
-		}
-
-		/* 2. Search for a command */
-		auto it = m_commands.find(name->toString());
-
-		if (it == m_commands.end()) {
-			// TODO: send error again
-			log::warning("command does not exists");
-			return;
-		}
-
-		/* 3. Try to execute it */
-		json::Value response = json::object({});
-
-		try {
-			response = it->second->exec(*this, object);
-
-			/* Adjust if command has returned something else */
-			if (!response.isObject()) {
-				response = json::object({});
-			}
-			
-			response.insert("status", true);
-		} catch (const std::exception &ex) {
-			response.insert("status", false);
-			response.insert("error", ex.what());
-		}
-
-		/* 4. Store the command name result */
-		response.insert("response", it->first);
-
-		/* 5. Send the result */
-		tc->send(response.toJson(0));
-	});
-}
-
-void Irccd::handleTransportDie(std::weak_ptr<TransportClient> ptr)
-{
-	post([=] (Irccd &) {
-		log::info("transport: client disconnected");
-
-		auto tc = ptr.lock();
-
-		if (tc) {
-			m_transportClients.erase(std::find(m_transportClients.begin(), m_transportClients.end(), tc));
-		}
-	});
-}
-
-void Irccd::processTransportClients(fd_set &input, fd_set &output)
-{
-	for (auto &client : m_transportClients) {
-		client->sync(input, output);
-	}
-}
-
-void Irccd::processTransportServers(fd_set &input)
-{
-	for (auto &transport : m_transportServers) {
-		if (!FD_ISSET(transport->handle(), &input)) {
-			continue;
-		}
-
-		log::debug("transport: new client connected");
-
-		std::shared_ptr<TransportClient> client = transport->accept();
-		std::weak_ptr<TransportClient> ptr(client);
-
-		/* Send some information */
-		json::Value object = json::object({
-			{ "program",	"irccd"			},
-			{ "major",	IRCCD_VERSION_MAJOR	},
-			{ "minor",	IRCCD_VERSION_MINOR	},
-			{ "patch",	IRCCD_VERSION_PATCH	}
-		});
-
-#if defined(WITH_JS)
-		object.insert("javascript", true);
-#endif
-#if defined(WITH_SSL)
-		object.insert("ssl", true);
-#endif
-
-		client->send(object.toJson(0));
-
-		/* Connect signals */
-		client->onCommand.connect(std::bind(&Irccd::handleTransportCommand, this, ptr, _1));
-		client->onDie.connect(std::bind(&Irccd::handleTransportDie, this, ptr));
-
-		/* Register it */
-		m_transportClients.push_back(std::move(client));
-	}
-}
-
-void Irccd::process(fd_set &setinput, fd_set &setoutput)
-{
-	// TODO: create services for transports
-	for (const auto &service : m_services) {
-		service->sync(setinput, setoutput);
-	}
-
-	/* 2. Check for transport clients */
-	processTransportClients(setinput, setoutput);
-
-	/* 3. Check for transport servers */
-	processTransportServers(setinput);
-}
-
 Irccd::Irccd()
 	: m_interruptService(std::make_shared<InterruptService>())
 	, m_serverService(std::make_shared<ServerService>(*this))
+	, m_transportService(std::make_shared<TransportService>(*this))
 {
 	m_services.push_back(m_interruptService);
 	m_services.push_back(m_serverService);
+	m_services.push_back(m_transportService);
 }
 
 void Irccd::post(std::function<void (Irccd &)> ev) noexcept
@@ -179,19 +56,6 @@
 	m_interruptService->interrupt();
 }
 
-void Irccd::addTransport(std::shared_ptr<TransportServer> ts)
-{
-	m_transportServers.push_back(std::move(ts));
-}
-
-void Irccd::broadcast(std::string data)
-{
-	// Asynchronous send.
-	for (auto &client : m_transportClients) {
-		client->send(data);
-	}
-}
-
 #if defined(WITH_JS)
 
 std::shared_ptr<Plugin> Irccd::getPlugin(const std::string &name) const noexcept
@@ -363,36 +227,14 @@
 	fd_set setoutput;
 	net::Handle max = 0;
 
-	auto set = [&] (fd_set &set, net::Handle handle) {
-		FD_SET(handle, &set);
-
-		if (handle > max)
-			max = handle;
-	};
-
 	FD_ZERO(&setinput);
 	FD_ZERO(&setoutput);
 
-	// TODO: create services for transports
 	for (const auto &service : m_services) {
 		service->prepare(setinput, setoutput, max);
 	}
 
-	/* 3. Add transports clients */
-	for (auto &client : m_transportClients) {
-		set(setinput, client->handle());
-
-		if (client->hasOutput()) {
-			set(setoutput, client->handle());
-		}
-	}
-
-	/* 4. Add transport servers */
-	for (auto &transport : m_transportServers) {
-		set(setinput, transport->handle());
-	}
-
-	/* 5. Do the selection */
+	// Do the selection.
 	struct timeval tv;
 
 	tv.tv_sec = 5;
@@ -400,18 +242,21 @@
 
 	int error = select(max + 1, &setinput, &setoutput, nullptr, &tv);
 
-	/* Skip anyway */
+	// Skip anyway if requested to stop
 	if (!m_running) {
 		return;
 	}
 
-	/* Skip on error */
+	// Skip on error.
 	if (error < 0 && errno != EINTR) {
 		log::warning() << "irccd: " << net::error(error) << endl;
 		return;
 	}
 
-	process(setinput, setoutput);
+	// Process after selection.
+	for (const auto &service : m_services) {
+		service->sync(setinput, setoutput);
+	}
 }
 
 void Irccd::dispatch()
@@ -426,8 +271,6 @@
 		std::lock_guard<mutex> lock(m_mutex);
 
 		copy = move(m_events);
-
-		/* Clear for safety */
 		m_events.clear();
 	}
 
@@ -445,6 +288,7 @@
 	log::debug() << "irccd: requesting to stop now" << endl;
 
 	m_running = false;
+	m_interruptService->interrupt();
 }
 
 } // !irccd
--- a/lib/irccd/irccd.hpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/irccd.hpp	Wed May 11 13:00:40 2016 +0200
@@ -33,7 +33,6 @@
 #include <mutex>
 #include <vector>
 
-#include "sockets.hpp"
 #include "sysconfig.hpp"
 
 #if defined(WITH_JS)
@@ -43,8 +42,6 @@
 #include "application.hpp"
 #include "logger.hpp"
 #include "rule.hpp"
-#include "server.hpp"
-#include "transport-server.hpp"
 
 namespace irccd {
 
@@ -53,21 +50,11 @@
 class Plugin;
 class ServerService;
 class Service;
-class TransportCommand;
+class TransportService;
 
 /**
  * \class Irccd
- * \brief Irccd main instance
- *
- * This class is used as the main application event loop, it stores servers, plugins and transports.
- *
- * In a general manner, no code in irccd is thread-safe because irccd is mono-threaded except the JavaScript timer
- * API.
- *
- * If you plan to add more threads to irccd, then the simpliest and safest way to execute thread-safe code is to
- * register an event using Irccd::post function which will be called during the event loop dispatching.
- *
- * Thus, except noticed as thread-safe, no function is assumed to be.
+ * \brief Irccd main instance.
  */
 class Irccd : public Application {
 private:
@@ -84,23 +71,13 @@
 	// Rules.
 	std::vector<Rule> m_rules;
 
-	// Transports.
-	std::vector<std::shared_ptr<TransportClient>> m_transportClients;
-	std::vector<std::shared_ptr<TransportServer>> m_transportServers;
-
-	// Services
+	// Services.
 	std::shared_ptr<InterruptService> m_interruptService;
 	std::shared_ptr<ServerService> m_serverService;
+	std::shared_ptr<TransportService> m_transportService;
 	std::vector<std::shared_ptr<Service>> m_services;
 
 	/*
-	 * Transport clients slots
-	 * ----------------------------------------------------------
-	 */
-	void handleTransportCommand(std::weak_ptr<TransportClient>, const json::Value &);
-	void handleTransportDie(std::weak_ptr<TransportClient>);
-
-	/*
 	 * Plugin timers slots
 	 * ----------------------------------------------------------
 	 *
@@ -112,17 +89,7 @@
 	void handleTimerEnd(std::weak_ptr<Plugin>, std::shared_ptr<Timer>);
 #endif
 
-	/*
-	 * Process the socket sets.
-	 * ----------------------------------------------------------
-	 *
-	 * These functions are called after polling which sockets are ready for reading/writing.
-	 */
-
-	void processTransportClients(fd_set &input, fd_set &output);
-	void processTransportServers(fd_set &input);
-	void process(fd_set &setinput, fd_set &setoutput);
-
+	// Not copyable and not movable because services has references to irccd.
 	Irccd(const Irccd &) = delete;
 	Irccd(Irccd &&) = delete;
 
@@ -137,6 +104,8 @@
 
 	/**
 	 * Add a generic service.
+	 *
+	 * \param service the service
 	 */
 	inline void addService(std::shared_ptr<Service> service)
 	{
@@ -154,6 +123,16 @@
 	}
 
 	/**
+	 * Access the transport service.
+	 *
+	 * \return the service
+	 */
+	inline TransportService &transportService() noexcept
+	{
+		return *m_transportService;
+	}
+
+	/**
 	 * Add an event to the queue. This will immediately signals the event loop to interrupt itself to dispatch
 	 * the pending events.
 	 *
@@ -163,27 +142,6 @@
 	void post(std::function<void (Irccd &)> ev) noexcept;
 
 	/*
-	 * Transport management
-	 * ----------------------------------------------------------
-	 *
-	 * Functions for adding new transport servers.
-	 */
-
-	/**
-	 * Add a transport server.
-	 *
-	 * \param ts the transport server
-	 */
-	void addTransport(std::shared_ptr<TransportServer> ts);
-
-	/**
-	 * Send data to all clients.
-	 *
-	 * \param data the data
-	 */
-	void broadcast(std::string data);
-
-	/*
 	 * Plugin management
 	 * ----------------------------------------------------------
 	 *
--- a/lib/irccd/service-server.cpp	Tue May 10 22:51:10 2016 +0200
+++ b/lib/irccd/service-server.cpp	Wed May 11 13:00:40 2016 +0200
@@ -22,8 +22,10 @@
 
 #include "irccd.hpp"
 #include "logger.hpp"
+#include "server.hpp"
 #include "server-event.hpp"
 #include "service-server.hpp"
+#include "service-transport.hpp"
 #include "sysconfig.hpp"
 #include "util.hpp"
 
@@ -45,7 +47,7 @@
 	log::debug() << "  mode: " << mode << "\n";
 	log::debug() << "  argument: " << arg << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onChannelMode"		},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -79,7 +81,7 @@
 	log::debug() << "  channel: " << channel << "\n";
 	log::debug() << "  message: " << message << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onChannelNotice"	},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -109,7 +111,7 @@
 
 	log::debug() << "server " << server->info().name << ": event onConnect" << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onConnect"		},
 		{ "server",	server->info().name	}
 	}).toJson(0));
@@ -139,7 +141,7 @@
 	log::debug() << "  channel: " << channel << "\n";
 	log::debug() << "  target: " << target << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onInvite"		},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -170,7 +172,7 @@
 	log::debug() << "  origin: " << origin << "\n";
 	log::debug() << "  channel: " << channel << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onJoin"		},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -203,7 +205,7 @@
 	log::debug() << "  target: " << target << "\n";
 	log::debug() << "  reason: " << reason << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onKick"		},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -237,7 +239,7 @@
 	log::debug() << "  channel: " << channel << "\n";
 	log::debug() << "  message: " << message << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onMessage"		},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -275,7 +277,7 @@
 	log::debug() << "  target: " << target << "\n";
 	log::debug() << "  message: " << message << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onMe"			},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -307,7 +309,7 @@
 	log::debug() << "  origin: " << origin << "\n";
 	log::debug() << "  mode: " << mode << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onMode"		},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -340,7 +342,7 @@
 
 	json::Value names(std::vector<json::Value>(nicknames.begin(), nicknames.end()));
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onNames"		},
 		{ "server",	server->info().name	},
 		{ "channel",	channel			},
@@ -371,7 +373,7 @@
 	log::debug() << "  origin: " << origin << "\n";
 	log::debug() << "  nickname: " << nickname << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onNick"		},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -402,7 +404,7 @@
 	log::debug() << "  origin: " << origin << "\n";
 	log::debug() << "  message: " << message << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onNotice"		},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -434,7 +436,7 @@
 	log::debug() << "  channel: " << channel << "\n";
 	log::debug() << "  reason: " << reason << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onPart"		},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -466,7 +468,7 @@
 	log::debug() << "  origin: " << origin << "\n";
 	log::debug() << "  message: " << message << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onQuery"		},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -503,7 +505,7 @@
 	log::debug() << "  channel: " << channel << "\n";
 	log::debug() << "  topic: " << topic << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "event",	"onTopic"		},
 		{ "server",	server->info().name	},
 		{ "origin",	origin			},
@@ -538,7 +540,7 @@
 	log::debug() << "  realname: " << whois.realname << "\n";
 	log::debug() << "  channels: " << util::join(whois.channels.begin(), whois.channels.end()) << std::endl;
 
-	m_irccd.broadcast(json::object({
+	m_irccd.transportService().broadcast(json::object({
 		{ "server",	server->info().name	},
 		{ "nickname",	whois.nick		},
 		{ "username",	whois.user		},
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/service-transport.cpp	Wed May 11 13:00:40 2016 +0200
@@ -0,0 +1,181 @@
+/*
+ * service-transport.cpp -- manage transport servers and clients
+ *
+ * Copyright (c) 2013-2016 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.hpp"
+#include "service-transport.hpp"
+#include "transport-client.hpp"
+#include "transport-server.hpp"
+
+namespace irccd {
+
+void TransportService::handleCommand(std::weak_ptr<TransportClient> ptr, const json::Value &object)
+{
+	assert(object.isObject());
+
+	m_irccd.post([=] (Irccd &) {
+		// 0. Be sure the object still exists.
+		auto tc = ptr.lock();
+
+		if (!tc) {
+			return;
+		}
+
+		// 1. Check if the Json object is valid.
+		auto name = object.find("command");
+		if (name == object.end() || name->typeOf() != json::Type::String) {
+			// TODO: send error
+			log::warning("invalid command object");
+			return;
+		}
+
+		// 2. Search for a command
+		auto it = m_irccd.commands().find(name->toString());
+
+		if (it == m_irccd.commands().end()) {
+			// TODO: send error again
+			log::warning("command does not exists");
+			return;
+		}
+
+		// 3. Try to execute it.
+		json::Value response = json::object({});
+
+		try {
+			response = it->second->exec(m_irccd, object);
+
+			// Adjust if command has returned something else.
+			if (!response.isObject()) {
+				response = json::object({});
+			}
+
+			response.insert("status", true);
+		} catch (const std::exception &ex) {
+			response.insert("status", false);
+			response.insert("error", ex.what());
+		}
+
+		// 4. Store the command name result.
+		response.insert("response", it->first);
+
+		// 5. Send the result.
+		tc->send(response.toJson(0));
+	});
+}
+
+void TransportService::handleDie(std::weak_ptr<TransportClient> ptr)
+{
+	m_irccd.post([=] (Irccd &) {
+		log::info("transport: client disconnected");
+
+		auto tc = ptr.lock();
+
+		if (tc) {
+			m_clients.erase(std::find(m_clients.begin(), m_clients.end(), tc));
+		}
+	});
+}
+
+TransportService::TransportService(Irccd &irccd) noexcept
+	: m_irccd(irccd)
+{
+}
+
+void TransportService::prepare(fd_set &in, fd_set &out, net::Handle &max)
+{
+	// Add transport servers.
+	for (const auto &transport : m_servers) {
+		FD_SET(transport->handle(), &in);
+
+		if (transport->handle() > max) {
+			max = transport->handle();
+		}
+	}
+
+	// Transport clients.
+	for (const auto &client : m_clients) {
+		FD_SET(client->handle(), &in);
+
+		if (client->hasOutput()) {
+			FD_SET(client->handle(), &out);
+		}
+		if (client->handle() > max) {
+			max = client->handle();
+		}
+	}
+}
+
+void TransportService::sync(fd_set &in, fd_set &out)
+{
+	using namespace std::placeholders;
+
+	// Transport servers.
+	for (const auto &transport : m_servers) {
+		if (!FD_ISSET(transport->handle(), &in)) {
+			continue;
+		}
+
+		log::debug("transport: new client connected");
+
+		std::shared_ptr<TransportClient> client = transport->accept();
+		std::weak_ptr<TransportClient> ptr(client);
+
+		// Send some information.
+		json::Value object = json::object({
+			{ "program",	"irccd"			},
+			{ "major",	IRCCD_VERSION_MAJOR	},
+			{ "minor",	IRCCD_VERSION_MINOR	},
+			{ "patch",	IRCCD_VERSION_PATCH	}
+		});
+
+#if defined(WITH_JS)
+		object.insert("javascript", true);
+#endif
+#if defined(WITH_SSL)
+		object.insert("ssl", true);
+#endif
+
+		client->send(object.toJson(0));
+
+		// Connect signals.
+		client->onCommand.connect(std::bind(&TransportService::handleCommand, this, ptr, _1));
+		client->onDie.connect(std::bind(&TransportService::handleDie, this, ptr));
+
+		// Register it.
+		m_clients.push_back(std::move(client));
+	}
+
+	// Transport clients.
+	for (const auto &client : m_clients) {
+		client->sync(in, out);
+	}
+}
+
+void TransportService::add(std::shared_ptr<TransportServer> ts)
+{
+	m_servers.push_back(std::move(ts));
+}
+
+void TransportService::broadcast(std::string data)
+{
+	// Asynchronous send.
+	for (const auto &client : m_clients) {
+		client->send(data);
+	}
+}
+
+} // !irccd
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/service-transport.hpp	Wed May 11 13:00:40 2016 +0200
@@ -0,0 +1,89 @@
+/*
+ * service-transport.hpp -- manage transport servers and clients
+ *
+ * Copyright (c) 2013-2016 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_SERVICE_TRANSPORT_HPP
+#define IRCCD_SERVICE_TRANSPORT_HPP
+
+/**
+ * \file service-transport.hpp
+ * \brief manage transport servers and clients.
+ */
+
+#include "service.hpp"
+
+namespace irccd {
+
+class TransportServer;
+class TransportClient;
+
+namespace json {
+
+class Value;
+
+} // !json
+
+/**
+ * \brief manage transport servers and clients.
+ */
+class TransportService : public Service {
+private:
+	Irccd &m_irccd;
+
+	std::vector<std::shared_ptr<TransportServer>> m_servers;
+	std::vector<std::shared_ptr<TransportClient>> m_clients;
+
+	void handleCommand(std::weak_ptr<TransportClient>, const json::Value &);
+	void handleDie(std::weak_ptr<TransportClient>);
+
+public:
+	/**
+	 * Create the transport service.
+	 *
+	 * \param irccd the irccd instance
+	 */
+	TransportService(Irccd &irccd) noexcept;
+
+	/**
+	 * \copydoc Service::prepare
+	 */
+	void prepare(fd_set &in, fd_set &out, net::Handle &max) override;
+
+	/**
+	 * \copydoc Service::sync
+	 */
+	void sync(fd_set &in, fd_set &out) override;
+
+	/**
+	 * Add a transport server.
+	 *
+	 * \param ts the transport server
+	 */
+	void add(std::shared_ptr<TransportServer> ts);
+
+	/**
+	 * Send data to all clients.
+	 *
+	 * \param data the data
+	 */
+	void broadcast(std::string data);
+
+};
+
+} // !irccd
+
+#endif // !IRCCD_SERVICE_TRANSPORT_HPP
\ No newline at end of file