changeset 92:4031eda60e11

Misc: switch to JSON, #650
author David Demelier <markand@malikania.fr>
date Wed, 07 Jun 2017 20:44:37 +0200
parents 79a2a75ca091
children 401161acfa60
files client/main.cpp libclient/CMakeLists.txt libclient/malikania/client/client.cpp libclient/malikania/client/client.hpp libclient/malikania/client/connection.cpp libclient/malikania/client/connection.hpp libclient/malikania/client/state.hpp libserver/CMakeLists.txt libserver/malikania/server/client.cpp libserver/malikania/server/client.hpp libserver/malikania/server/net/auth_handler.cpp libserver/malikania/server/net/auth_handler.hpp libserver/malikania/server/net/handler.hpp libserver/malikania/server/server.cpp libserver/malikania/server/server.hpp server/main.cpp
diffstat 16 files changed, 999 insertions(+), 227 deletions(-) [+]
line wrap: on
line diff
--- a/client/main.cpp	Tue Apr 25 12:44:18 2017 +0200
+++ b/client/main.cpp	Wed Jun 07 20:44:37 2017 +0200
@@ -16,37 +16,19 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <malikania/duktape.hpp>
-
-#include <chrono>
-#include <thread>
-
-#include <malikania/client/button.hpp>
-#include <malikania/client/color.hpp>
-#include <malikania/client/frame.hpp>
-#include <malikania/client/label.hpp>
-#include <malikania/client/point.hpp>
-#include <malikania/client/unique_layout.hpp>
-#include <malikania/client/window.hpp>
+#include <malikania/client/connection.hpp>
+#include <malikania/client/client.hpp>
 
 int main()
 {
-    mlk::client::window win;
+    boost::asio::io_service service;
 
-    auto f = std::make_shared<mlk::client::frame>();
-    auto w = std::make_shared<mlk::client::label>("Malikania");
-    auto l = std::make_shared<mlk::client::unique_layout>(w);
+    mlk::client::connection cn(service);
+    mlk::client::client client(service, cn);
 
-    f->move({50, 50});
-    f->set_layout(l);
-    win.add_frame(f);
+    client.connect("localhost", 3320);
 
-    while (win.is_open()) {
-        win.poll();
-        win.set_drawing_color({255, 255, 255, 255});
-        win.clear();
-        win.draw_frames();
-        win.present();
-        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+    for (;;) {
+        service.run();
     }
 }
--- a/libclient/CMakeLists.txt	Tue Apr 25 12:44:18 2017 +0200
+++ b/libclient/CMakeLists.txt	Wed Jun 07 20:44:37 2017 +0200
@@ -23,7 +23,9 @@
     ${libmlk-client_SOURCE_DIR}/malikania/client/animation.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/animator.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/button.hpp
+    ${libmlk-client_SOURCE_DIR}/malikania/client/client.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/color.hpp
+    ${libmlk-client_SOURCE_DIR}/malikania/client/connection.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/font.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/frame.hpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/image.hpp
@@ -51,7 +53,9 @@
     ${libmlk-client_SOURCE_DIR}/malikania/client/animator.cpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/button.cpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/loader.cpp
+    ${libmlk-client_SOURCE_DIR}/malikania/client/client.cpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/color.cpp
+    ${libmlk-client_SOURCE_DIR}/malikania/client/connection.cpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/font.cpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/frame.cpp
     ${libmlk-client_SOURCE_DIR}/malikania/client/image.cpp
@@ -98,5 +102,5 @@
         $<BUILD_INTERFACE:${libmlk-client_SOURCE_DIR}/malikania/client/${WITH_BACKEND_DIR}>
         $<BUILD_INTERFACE:${libmlk-client_SOURCE_DIR}/malikania>
         ${INCLUDES}
-    LIBRARIES libmlk-common ${LIBRARIES}
+    LIBRARIES libmlk-common OpenSSL::SSL OpenSSL::Crypto ${LIBRARIES}
 )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libclient/malikania/client/client.cpp	Wed Jun 07 20:44:37 2017 +0200
@@ -0,0 +1,63 @@
+/*
+ * client.cpp -- main client game clas
+ *
+ * Copyright (c) 2013-2017 Malikania Authors
+ *
+ * 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 <iostream>
+
+#include "client.hpp"
+#include "connection.hpp"
+#include "state.hpp"
+
+namespace mlk {
+
+namespace client {
+
+client::client(boost::asio::io_service& service, mlk::client::connection& connection) noexcept
+    : service_(service)
+    , connection_(connection)
+{
+}
+
+client::~client() noexcept = default;
+
+void client::handle_error(boost::system::error_code)
+{
+    // TODO: add error
+}
+
+void client::handle_connect(boost::system::error_code)
+{
+    connection_.do_read(*this);
+}
+
+void client::handle_read(boost::system::error_code, nlohmann::json msg)
+{
+    connection_.do_read(*this);
+
+    if (state_) {
+        state_->handle_message(*this, std::move(msg));
+    }
+}
+
+void client::connect(std::string host, uint16_t port)
+{
+    connection_.do_connect(*this, std::move(host), port);
+}
+
+} // !client
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libclient/malikania/client/client.hpp	Wed Jun 07 20:44:37 2017 +0200
@@ -0,0 +1,118 @@
+/*
+ * client.hpp -- main client game clas
+ *
+ * Copyright (c) 2013-2017 Malikania Authors
+ *
+ * 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 MALIKANIA_CLIENT_CLIENT_HPP
+#define MALIKANIA_CLIENT_CLIENT_HPP
+
+/**
+ * \file client.hpp
+ * \brief Main client game class.
+ */
+
+#include <boost/asio.hpp>
+
+#include <json.hpp>
+
+namespace mlk {
+
+namespace client {
+
+class connection;
+class state;
+
+/**
+ * \brief Main client game class
+ */
+class client {
+private:
+    boost::asio::io_service& service_;
+    mlk::client::connection& connection_;
+    std::unique_ptr<state> state_;
+
+public:
+    /**
+     * Construct the client, no connection attempt will be made yet.
+     *
+     * \param service the I/O service
+     * \param the connection object
+     */
+    client(boost::asio::io_service& service, mlk::client::connection& connection) noexcept;
+
+    /**
+     * Virtual destructor defaulted.
+     */
+    virtual ~client() noexcept;
+
+    /**
+     * Get the connection object.
+     *
+     * \return the connection
+     */
+    inline mlk::client::connection& connection() noexcept
+    {
+        return connection_;
+    }
+
+    /**
+     * Get the connection object.
+     *
+     * \return the connection
+     */
+    inline const mlk::client::connection& connection() const noexcept
+    {
+        return connection_;
+    }
+
+    /**
+     * Connect to the server.
+     *
+     * This is an alias of connection_.connect(*this, host, port);
+     *
+     * \param host the hostname
+     * \param port the port number
+     */
+    void connect(std::string host, std::uint16_t port);
+
+    /**
+     * Handle unexpected network error.
+     *
+     * \param code the error code
+     */
+    virtual void handle_error(boost::system::error_code code);
+
+    /**
+     * Handle connection.
+     *
+     * \param code the possible error code
+     */
+    virtual void handle_connect(boost::system::error_code code);
+
+    /**
+     * Handle reading operation.
+     *
+     * \param code the possible error code
+     * \param msg the received message
+     */
+    virtual void handle_read(boost::system::error_code, nlohmann::json msg);
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_CLIENT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libclient/malikania/client/connection.cpp	Wed Jun 07 20:44:37 2017 +0200
@@ -0,0 +1,130 @@
+/*
+ * connection.hpp -- connection to server
+ *
+ * Copyright (c) 2013-2017 Malikania Authors
+ *
+ * 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 <functional>
+
+#include "client.hpp"
+#include "connection.hpp"
+
+namespace mlk {
+
+namespace client {
+
+connection::connection(boost::asio::io_service& service)
+    : service_(service)
+    , context_(boost::asio::ssl::context::sslv23)
+    , socket_(service_, context_)
+{
+}
+
+void connection::handshake(client& client)
+{
+    socket_.async_handshake(boost::asio::ssl::stream_base::client, [this, &client] (auto code) {
+        if (code) {
+            client.handle_error(code);
+        } else {
+            client.handle_connect(code);
+        }
+    });
+}
+
+void connection::do_connect(client& client, std::string host, std::uint16_t port)
+{
+    using namespace std::placeholders;
+    using tcp = boost::asio::ip::tcp;
+
+    auto resolver = std::make_shared<tcp::resolver>(service_);
+    auto str = std::to_string(port);
+
+    resolver->async_resolve(tcp::resolver::query(host, str), [this, resolver, &client] (auto code, auto ep) {
+        if (code) {
+            client.handle_error(code);
+        } else {
+            boost::asio::async_connect(socket_.lowest_layer(), ep, [this, &client] (auto code, auto) {
+                if (code) {
+                    client.handle_error(code);
+                } else {
+                    handshake(client);
+                }
+            });
+        }
+    });
+}
+
+void connection::flush(client& client)
+{
+    auto buffer = boost::asio::buffer(output_.front().data(), output_.front().length());
+
+    boost::asio::async_write(socket_, buffer, [this, &client] (auto code, auto) {
+        output_.pop_front();
+
+        if (code) {
+            client.handle_error(code);
+        } else if (!output_.empty()) {
+            flush(client);
+        }
+    });
+}
+
+void connection::do_send(client& client, nlohmann::json message)
+{
+    assert(message.is_object());
+
+    auto in_progress = !output_.empty();
+
+    output_.push_back(message.dump() + "\r\n\r\n");
+
+    if (!in_progress) {
+        flush(client);
+    }
+}
+
+void connection::do_read(client& client)
+{
+#if !defined(NDEBUG)
+    assert(!is_reading);
+
+    is_reading = true;
+#endif
+
+    boost::asio::async_read_until(socket_, input_, "\r\n\r\n", [this, &client] (auto code, auto xfer) {
+#if !defined(NDEBUG)
+        is_reading = false;
+#endif
+        if (code || xfer == 0) {
+            client.handle_error(code);
+        } else {
+            std::string command{
+                boost::asio::buffers_begin(input_.data()),
+                boost::asio::buffers_begin(input_.data()) + xfer - /* \r\n\r\n */ 4
+            };
+
+            input_.consume(xfer);
+
+            try {
+                client.handle_read(std::move(code), nlohmann::json::parse(command));
+            } catch (const std::exception& ex) {
+                // TODO: log error
+            }
+        }
+    });
+}
+
+} // !client
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libclient/malikania/client/connection.hpp	Wed Jun 07 20:44:37 2017 +0200
@@ -0,0 +1,96 @@
+/*
+ * connection.hpp -- connection to server
+ *
+ * Copyright (c) 2013-2017 Malikania Authors
+ *
+ * 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 MALIKANIA_CLIENT_CONNECTION_HPP
+#define MALIKANIA_CLIENT_CONNECTION_HPP
+
+/**
+ * \file connection.hpp
+ * \brief Connection to server.
+ */
+
+#include <deque>
+
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+
+#include <json.hpp>
+
+namespace mlk {
+
+namespace client {
+
+class client;
+
+/**
+ * \brief connection to server.
+ */
+class connection {
+public:
+private:
+    boost::asio::io_service& service_;
+    boost::asio::ssl::context context_;
+    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
+    boost::asio::streambuf input_;
+    std::deque<std::string> output_;
+
+#if !defined(NDEBUG)
+    bool is_reading{false};
+#endif
+
+    void handshake(client&);
+    void flush(client&);
+
+public:
+    /**
+     * Create a connection object.
+     */
+    connection(boost::asio::io_service& service);
+
+    /**
+     * Connect to the given end point.
+     *
+     * \pre handler != nullptr
+     * \param host the hostname
+     * \param port the port number
+     * \param handler the handler
+     */
+    virtual void do_connect(client& client, std::string host, std::uint16_t port);
+
+    /**
+     * Send the given message to the server.
+     *
+     * \pre message.is_object()
+     * \pre handler != nullptr
+     * \param message the message object
+     */
+    virtual void do_send(client& client, nlohmann::json message);
+
+    /**
+     * Request for a read operation.
+     *
+     * \pre no reading operation must be pending
+     */
+    virtual void do_read(client& client);
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_CONNECTION_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libclient/malikania/client/state.hpp	Wed Jun 07 20:44:37 2017 +0200
@@ -0,0 +1,67 @@
+/*
+ * state.hpp -- game client state
+ *
+ * Copyright (c) 2013-2017 Malikania Authors
+ *
+ * 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 MALIKANIA_CLIENT_STATE_HPP
+#define MALIKANIA_CLIENT_STATE_HPP
+
+/**
+ * \file state.hpp
+ * \brief Game client state.
+ */
+
+#include <memory>
+
+namespace mlk {
+
+namespace client {
+
+class client;
+
+/**
+ * \brief Game client state.
+ */
+class state {
+public:
+    /**
+     * Default constructor.
+     */
+    state() noexcept = default;
+
+    /**
+     * Virtual destructor defaulted.
+     */
+    virtual ~state() noexcept = default;
+
+    /**
+     * Handle a network message.
+     *
+     * \param client the client
+     * \param msg the network message
+     */
+    virtual void handle_message(client& client, nlohmann::json msg)
+    {
+        (void)client;
+        (void)msg;
+    }
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_STATE_HPP
--- a/libserver/CMakeLists.txt	Tue Apr 25 12:44:18 2017 +0200
+++ b/libserver/CMakeLists.txt	Wed Jun 07 20:44:37 2017 +0200
@@ -20,6 +20,7 @@
 
 set(
     HEADERS
+    ${libmlk-server_SOURCE_DIR}/malikania/server/net/auth_handler.hpp
     ${libmlk-server_SOURCE_DIR}/malikania/server/account.hpp
     ${libmlk-server_SOURCE_DIR}/malikania/server/account_dao.hpp
     ${libmlk-server_SOURCE_DIR}/malikania/server/client.hpp
@@ -29,6 +30,7 @@
 
 set(
     SOURCES
+    ${libmlk-server_SOURCE_DIR}/malikania/server/net/auth_handler.cpp
     ${libmlk-server_SOURCE_DIR}/malikania/server/account_dao.cpp
     ${libmlk-server_SOURCE_DIR}/malikania/server/client.cpp
     ${libmlk-server_SOURCE_DIR}/malikania/server/database.cpp
@@ -47,4 +49,6 @@
     PUBLIC_INCLUDES
         ${Boost_INCLUDE_DIRS}
         ${libmlk-server_SOURCE_DIR}
+    PRIVATE_INCLUDES
+        ${libmlk-server_SOURCE_DIR}/malikania/server
 )
--- a/libserver/malikania/server/client.cpp	Tue Apr 25 12:44:18 2017 +0200
+++ b/libserver/malikania/server/client.cpp	Wed Jun 07 20:44:37 2017 +0200
@@ -26,59 +26,26 @@
 
 namespace server {
 
-/*
- * client_proxy_writer
- * ------------------------------------------------------------------
- */
-
-client_proxy_writer::client_proxy_writer(std::shared_ptr<client> client, std::string init)
-    : m_client(std::move(client))
-{
-    assert(m_client);
-
-    m_output << init;
-}
-
-client_proxy_writer::~client_proxy_writer()
-{
-    m_client->send(m_output.str());
-}
-
-/*
- * client_proxy_writer
- * ------------------------------------------------------------------
- */
-
 void client::handle_read(boost::system::error_code code, std::size_t xfer)
 {
     // TODO: use fixed size stream + verify exceed.
     if (code) {
-        m_server.handle_disconnect(shared_from_this());
+        server_.handle_disconnect(shared_from_this());
     } else {
-        std::istringstream iss(std::string(
-            boost::asio::buffers_begin(m_input.data()),
-            boost::asio::buffers_begin(m_input.data()) + xfer - 4
-        ));
+        std::string message(
+            boost::asio::buffers_begin(input_.data()),
+            boost::asio::buffers_begin(input_.data()) + xfer - 4
+        );
 
         // Remove early in case of errors.
-        m_input.consume(xfer);
+        input_.consume(xfer);
 
-        // Extract command name.
-        std::string cmd;
-        iss >> cmd >> std::ws;
-
-        if (!iss) {
-            return;
+        try {
+            server_.handle_read(shared_from_this(), nlohmann::json::parse(message));
+        } catch (const std::exception& ex) {
+            std::cerr << ex.what() << std::endl;
         }
 
-        // Rest of the data.
-        std::string data(std::istreambuf_iterator<char>(iss), {});
-
-        if (!iss) {
-            return;
-        }
-
-        m_server.handle_message(shared_from_this(), std::move(cmd), std::move(data));
         do_read();
     }
 }
@@ -86,12 +53,14 @@
 void client::handle_write(boost::system::error_code code, std::size_t)
 {
     if (code) {
-        m_server.handle_disconnect(shared_from_this());
+        server_.handle_disconnect(shared_from_this());
     } else {
-        m_output.pop_front();
+        output_.pop_front();
 
-        if (!m_output.empty()) {
+        if (!output_.empty()) {
             do_write();
+        } else if (state_ == state::closing) {
+            server_.handle_disconnect(shared_from_this());
         }
     }
 }
@@ -100,25 +69,25 @@
 {
     auto self = shared_from_this();
 
-    boost::asio::async_read_until(m_socket, m_input, "\r\n\r\n", [self] (auto code, auto xfer) {
+    boost::asio::async_read_until(socket_, input_, "\r\n\r\n", [self] (auto code, auto xfer) {
         self->handle_read(code, xfer);
     });
 }
 
 void client::do_write()
 {
-    assert(!m_output.empty());
+    assert(!output_.empty());
 
     auto self = shared_from_this();
 
-    boost::asio::async_write(m_socket, boost::asio::buffer(m_output[0]), [self] (auto code, auto xfer) {
+    boost::asio::async_write(socket_, boost::asio::buffer(output_[0]), [self] (auto code, auto xfer) {
         self->handle_write(code, xfer);
     });
 }
 
 client::client(server& server, boost::asio::io_service& service, boost::asio::ssl::context& context)
-    : m_server(server)
-    , m_socket(service, context)
+    : server_(server)
+    , socket_(service, context)
 {
 }
 
@@ -126,7 +95,7 @@
 {
     auto self = shared_from_this();
 
-    m_socket.async_handshake(boost::asio::ssl::stream_base::server, [self] (auto code) {
+    socket_.async_handshake(boost::asio::ssl::stream_base::server, [self] (auto code) {
         if (code) {
             std::cerr << "handshake failure: " << code << std::endl;
         } else {
@@ -140,40 +109,59 @@
     do_read();
 }
 
-void client::send(std::string cmd, std::string data)
-{
-    send_raw(cmd + " " + data);
-}
-
-void client::ok(std::string cmd)
-{
-    send_raw(cmd + " ok");
-}
-
-void client::error(std::string cmd, std::string reason)
+void client::send(nlohmann::json message)
 {
-    send_raw(cmd + " error " + reason);
-}
-
-client_proxy_writer client::send(std::string cmd)
-{
-    return client_proxy_writer(shared_from_this(), cmd + " ");
-}
+    assert(message.is_object());
 
-void client::send_raw(std::string data)
-{
-    assert(!data.empty());
+    if (state_ == state::closing) {
+        return;
+    }
 
-    auto in_progress = !m_output.empty();
+    auto in_progress = !output_.empty();
 
-    data += "\r\n\r\n";
-    m_output.push_back(std::move(data));
+    output_.push_back(message.dump() + "\r\n\r\n");
 
     if (!in_progress) {
         do_write();
     }
 }
 
+void client::ok(std::string cmd, nlohmann::json message)
+{
+    assert(message.is_null() || message.is_object());
+
+    if (!message.is_object()) {
+        message = nlohmann::json::object();
+    }
+
+    message["command"] = std::move(cmd);
+    message["status"] = true;
+
+    send(std::move(message));
+}
+
+void client::error(std::string cmd, nlohmann::json args)
+{
+    assert(args.is_object() || args.is_string());
+
+    if (args.is_string()) {
+        args = nlohmann::json::object({
+            { "error", args.get<std::string>() }
+        });
+    }
+
+    args["command"] = std::move(cmd);
+    args["status"] = false;
+
+    send(std::move(args));
+}
+
+void client::fatal(std::string cmd, nlohmann::json args)
+{
+    error(std::move(cmd), std::move(args));
+    state_ = state::closing;
+}
+
 } // !server
 
 } // !mlk
--- a/libserver/malikania/server/client.hpp	Tue Apr 25 12:44:18 2017 +0200
+++ b/libserver/malikania/server/client.hpp	Wed Jun 07 20:44:37 2017 +0200
@@ -19,6 +19,11 @@
 #ifndef MALIKANIA_SERVER_CLIENT_HPP
 #define MALIKANIA_SERVER_CLIENT_HPP
 
+/**
+ * \file client.hpp
+ * \brief Client connected to the server.
+ */
+
 #include <cstdint>
 #include <deque>
 #include <memory>
@@ -28,48 +33,51 @@
 #include <boost/asio.hpp>
 #include <boost/asio/ssl.hpp>
 
+#include <json.hpp>
+
 namespace mlk {
 
 namespace server {
 
-class client;
 class server;
 
-class client_proxy_writer {
-    std::shared_ptr<client> m_client;
-    std::ostringstream m_output;
-
-public:
-    client_proxy_writer(std::shared_ptr<client> client, std::string init);
-
-    client_proxy_writer(client_proxy_writer&&) = default;
-
-    ~client_proxy_writer();
-
-    template <typename Arg>
-    inline client_proxy_writer& operator<<(Arg&& arg)
-    {
-        m_output << std::forward<Arg>(arg);
-
-        return *this;
-    }
-};
-
+/**
+ * \brief Client connected to the server.
+ */
 class client : public std::enable_shared_from_this<client> {
 public:
     friend class server;
 
+    /**
+     * \brief Describe client current state
+     */
     enum class state : std::uint8_t {
+        /**
+         * The client is authenticating.
+         */
         authenticating,
-        ready
+
+        /**
+         * The client is ready for I/O
+         */
+        ready,
+
+        /**
+         * The client is closing.
+         *
+         * No more messages are accepted for output, the pending queue is sent
+         * to the client and then disconnected.
+         */
+        closing
     };
 
 private:
-    server& m_server;
-    state m_state{state::authenticating};
-    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> m_socket;
-    boost::asio::streambuf m_input;
-    std::deque<std::string> m_output;
+    server& server_;
+    state state_{state::authenticating};
+
+    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
+    boost::asio::streambuf input_;
+    std::deque<std::string> output_;
 
     void handle_read(boost::system::error_code, std::size_t);
     void handle_write(boost::system::error_code, std::size_t);
@@ -91,49 +99,64 @@
     client(server& server, boost::asio::io_service& service, boost::asio::ssl::context& context);
 
     /**
+     * Get the client state.
+     *
+     * \return the state
+     */
+    inline enum state state() const noexcept
+    {
+        return state_;
+    }
+
+    /**
+     * Set the client state.
+     *
+     * \param new_state the state
+     */
+    inline void set_state(enum state new_state) noexcept
+    {
+        state_ = new_state;
+    }
+
+    /**
      * Send a command and its arguments.
      *
      * \pre !cmd.empty()
-     * \param cmd the command
-     * \param args the arguments
+     * \param message the message to send
      */
-    void send(std::string cmd, std::string args);
+    void send(nlohmann::json message);
 
     /**
      * Send successful command result.
      *
      * \pre !cmd.empty()
+     * \pre args.is_object() || args.is_object()
      * \param cmd the command name
+     * \param args the extra data
      */
-    void ok(std::string cmd);
+    void ok(std::string cmd, nlohmann::json args = nullptr);
 
     /**
      * Send a error command result.
      *
      * \pre !cmd.empty() && !reason.empty()
+     * \pre args.is_object() || args.is_string()
      * \param cmd the command name
-     * \param reason the reason string
+     * \param args the extra data (can be a string)
      */
-    void error(std::string cmd, std::string reason);
+    void error(std::string cmd, nlohmann::json args);
 
     /**
-     * Convenient function for sending data using operator<<.
+     * Send a fatal error result.
+     *
+     * The client state is changed to closing.
      *
-     * The returned have operator<< defined for the user and will append all
-     * data inserted that way when the appropriate client_proxy_write is
-     * destroyed.
-     *
+     * \pre !cmd.empty() && !reason.empty()
+     * \pre args.is_object() || args.is_string()
      * \param cmd the command name
+     * \param args the extra data (can be a string)
      */
-    client_proxy_writer send(std::string cmd);
-
-    /**
-     * Send a raw data.
-     *
-     * \pre !data.empty()
-     * \param data the data
-     */
-    void send_raw(std::string data);
+    void fatal(std::string cmd, nlohmann::json args);
 };
 
 } // !server
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libserver/malikania/server/net/auth_handler.cpp	Wed Jun 07 20:44:37 2017 +0200
@@ -0,0 +1,51 @@
+/*
+ * auth_handler.cpp -- handle "auth" network command
+ *
+ * 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 <util.hpp>
+
+#include "account_dao.hpp"
+#include "auth_handler.hpp"
+#include "client.hpp"
+#include "server.hpp"
+
+namespace mlk {
+
+namespace server {
+
+void auth_handler::exec(server& server, std::shared_ptr<client> clt, nlohmann::json object) noexcept
+{
+    try {
+        auto login = util::json::require_string(object, "/login"_json_pointer);
+        auto password = util::json::require_string(object, "/password"_json_pointer);
+
+        account_dao dao(server.database());
+
+        if (!dao.authenticate(login, password)) {
+            clt->error("auth", "invalid credentials");
+        } else {
+            clt->set_state(client::state::ready);
+            clt->ok("auth");
+        }
+    } catch (const std::exception& ex) {
+        clt->error("auth", ex.what());
+    }
+}
+
+} // !server
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libserver/malikania/server/net/auth_handler.hpp	Wed Jun 07 20:44:37 2017 +0200
@@ -0,0 +1,48 @@
+/*
+ * auth_handler.hpp -- handle "auth" network command
+ *
+ * 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 MALIKANIA_SERVER_AUTH_HANDLER_HPP
+#define MALIKANIA_SERVER_AUTH_HANDLER_HPP
+
+/**
+ * \file auth_handler.hpp
+ * \brief Handle "auth" network command.
+ */
+
+#include "handler.hpp"
+
+namespace mlk {
+
+namespace server {
+
+/**
+ * \brief Handle "auth" network command.
+ */
+class auth_handler : public handler {
+public:
+    /**
+     * \copydoc handler::exec
+     */
+    void exec(server &server, std::shared_ptr<client> clt, nlohmann::json object) noexcept override;
+};
+
+} // !server
+
+} // !mlk
+
+#endif // !MALIKANIA_SERVER_AUTH_HANDLER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libserver/malikania/server/net/handler.hpp	Wed Jun 07 20:44:37 2017 +0200
@@ -0,0 +1,69 @@
+/*
+ * handler.hpp -- server side remote command
+ *
+ * Copyright (c) 2013-2017 Malikania Authors
+ *
+ * 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 MALIKANIA_SERVER_HANDLER_HPP
+#define MALIKANIA_SERVER_HANDLER_HPP
+
+/**
+ * \file handler.hpp
+ * \brief Server side remote command.
+ */
+
+#include <memory>
+
+#include <json.hpp>
+
+namespace mlk {
+
+namespace server {
+
+class server;
+class client;
+
+/**
+ * \brief Server side remote command.
+ */
+class handler {
+public:
+    /**
+     * Default constructor.
+     */
+    handler() noexcept = default;
+
+    /**
+     * Virtual destructor defaulted.
+     */
+    virtual ~handler() noexcept = default;
+
+    /**
+     * Execute the function from the given client.
+     *
+     * \param server the global object server
+     * \param client the client
+     * \param message the network message
+     */
+    virtual void exec(server& server,
+                      std::shared_ptr<client> client,
+                      nlohmann::json message) noexcept = 0;
+};
+
+} // !server
+
+} // !mlk
+
+#endif // !MALIKANIA_SERVER_HANDLER_HPP
--- a/libserver/malikania/server/server.cpp	Tue Apr 25 12:44:18 2017 +0200
+++ b/libserver/malikania/server/server.cpp	Wed Jun 07 20:44:37 2017 +0200
@@ -23,85 +23,134 @@
 #include "server.hpp"
 #include "util.hpp"
 
+#include "net/auth_handler.hpp"
+
 namespace mlk {
 
 namespace server {
 
-void server::handle_auth(std::shared_ptr<client> clt, std::string args)
-{
-    std::cout << "== auth ==" << std::endl;
-
-    auto list = util::net::split(args);
+/*
+ * server::load
+ * ------------------------------------------------------------------
+ */
 
-    if (list.size() != 2) {
-        clt->error("auth", "2 arguments required");
-    } else {
-        account_dao dao(m_database);
+void server::load()
+{
+    handle("auth", std::make_unique<auth_handler>());
+}
 
-        if (!dao.authenticate(list[0], list[1])) {
-            clt->error("auth", "invalid credential or inexistant account");
-        } else {
-            clt->ok("auth");
-        }
-    }
-}
+/*
+ * server::start
+ * ------------------------------------------------------------------
+ *
+ * Start an asynchronous accept(2) call.
+ */
 
 void server::start()
 {
-    auto clt = std::make_shared<client>(*this, m_service, m_context);
+    auto clt = std::make_shared<client>(*this, service_, context_);
 
-    m_acceptor.async_accept(clt->m_socket.lowest_layer(), [this, clt] (auto code) {
+    acceptor_.async_accept(clt->socket_.lowest_layer(), [this, clt] (auto code) {
         this->handle_accept(std::move(clt), code);
     });
 }
 
-boost::asio::ip::tcp::endpoint server::endpoint(const server_settings &params) const
+/*
+ * server::endpoint
+ * ------------------------------------------------------------------
+ *
+ * Create an endpoint according to the parameters.
+ */
+
+boost::asio::ip::tcp::endpoint server::endpoint(const settings& params) const
 {
     // TODO: add more settings there.
-    return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), params.port);
+    return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), params.port());
 }
 
-server::server(boost::asio::io_service& service,
-               const server_settings& sv_params,
-               const database_settings& db_params)
-    : m_service(service)
-    , m_acceptor(service, endpoint(sv_params))
-    , m_context(boost::asio::ssl::context::sslv23)
-    , m_handlers{
-        { "auth", std::bind(&server::handle_auth, this, std::placeholders::_1, std::placeholders::_2) }
-    }
-    , m_database(db_params)
+/*
+ * server
+ * ------------------------------------------------------------------
+ *
+ * Construct a server object and start listening for clients.
+ */
+
+server::server(boost::asio::io_service& service, mlk::server::database& db, const settings& settings)
+    : service_(service)
+    , acceptor_(service, endpoint(settings))
+    , context_(boost::asio::ssl::context::sslv23)
+    , database_(db)
 {
-    m_context.use_certificate_chain_file(sv_params.certificate);
-    m_context.use_private_key_file(sv_params.key, boost::asio::ssl::context::pem);
+    context_.use_certificate_chain_file(settings.certificate());
+    context_.use_private_key_file(settings.key(), boost::asio::ssl::context::pem);
 
+    load();
     start();
 }
 
-void server::add_handler(std::string cmd, handler func)
+/*
+ * server::handle
+ * ------------------------------------------------------------------
+ */
+
+void server::handle(std::string name, std::unique_ptr<handler> handler)
 {
-    m_handlers.emplace(std::move(cmd), std::move(func));
+    assert(!name.empty());
+    assert(handler);
+
+    handlers_.emplace(std::move(name), std::move(handler));
 }
 
+/*
+ * server::handle_disconnect
+ * ------------------------------------------------------------------
+ *
+ * Asynchronous function called when a client has been detected as disconnected.
+ * The default implementation just removes the client from the server.
+ */
+
 void server::handle_disconnect(std::shared_ptr<client> clt)
 {
     std::cout << "client disconnected" << std::endl;
-    m_clients.erase(clt);
+    clients_.erase(std::move(clt));
 }
 
-void server::handle_message(std::shared_ptr<client> clt, std::string cmd, std::string args)
+/*
+ * server::handle_read
+ * ------------------------------------------------------------------
+ */
+
+void server::handle_read(std::shared_ptr<client> clt, nlohmann::json message)
 {
-    std::cout << "client sent:\n";
-    std::cout << "  -> cmd [" << cmd << "]\n";
-    std::cout << "  -> args [" << args << "]\n";
+    assert(message.is_object());
+
+    auto cmd = message.find("command");
 
-    auto it = m_handlers.find(cmd);
+    if (cmd == message.end() || !cmd->is_string()) {
+        std::cerr << "client sent invalid message" << std::endl;
+        return;
+    }
 
-    if (it != m_handlers.end()) {
-        it->second(std::move(clt), std::move(args));
+    auto it = handlers_.find(*cmd);
+
+    // TODO: try-catch
+    if (it == handlers_.end()) {
+        std::cerr << "client send an unknown or unhandled command" << std::endl;
+    } else {
+        it->second->exec(*this, std::move(clt), std::move(message));
     }
 }
 
+/*
+ * server::handle_accept
+ * ------------------------------------------------------------------
+ *
+ * Asynchronous function called once an accept(2) called has been finished with
+ * or without errors.
+ *
+ * On success, request a handshake on the client.
+ */
+
 void server::handle_accept(std::shared_ptr<client> clt, boost::system::error_code code)
 {
     if (code) {
@@ -109,7 +158,7 @@
     } else {
         std::cout << "new client connected" << std::endl;
         clt->handshake();
-        m_clients.insert(std::move(clt));
+        clients_.insert(std::move(clt));
     }
 
     start();
--- a/libserver/malikania/server/server.hpp	Tue Apr 25 12:44:18 2017 +0200
+++ b/libserver/malikania/server/server.hpp	Wed Jun 07 20:44:37 2017 +0200
@@ -20,7 +20,6 @@
 #define MALIKANIA_SERVER_SERVER_HPP
 
 #include <cstdint>
-#include <functional>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -29,51 +28,131 @@
 #include <boost/asio.hpp>
 #include <boost/asio/ssl.hpp>
 
-#include "database.hpp"
+#include <json.hpp>
+
+#include "net/handler.hpp"
 
 namespace mlk {
 
 namespace server {
 
 class client;
+class database;
 
-class server_settings {
+/**
+ * \brief Server parameters
+ */
+class settings {
+private:
+    std::uint16_t port_;
+    std::string certificate_;
+    std::string key_;
+
 public:
-    std::uint16_t port;
-    std::string certificate;
-    std::string key;
+    /**
+     * Constructor.
+     *
+     * \param port the port
+     * \param certificate the certificate file
+     * \param key the private key file
+     * \pre !certificate.empty()
+     * \pre !key.empty()
+     */
+    inline settings(std::uint16_t port, std::string certificate, std::string key) noexcept
+        : port_(port)
+        , certificate_(std::move(certificate))
+        , key_(std::move(key))
+    {
+        assert(!certificate_.empty());
+        assert(!key_.empty());
+    }
+
+    /**
+     * Get the associated port.
+     *
+     * \return the port
+     */
+    inline std::uint16_t port() const noexcept
+    {
+        return port_;
+    }
+
+    /**
+     * Get the certificate file.
+     *
+     * \return the path to the certificate file
+     */
+    inline const std::string& certificate() const noexcept
+    {
+        return certificate_;
+    }
+
+    /**
+     * Get the key file.
+     *
+     * \return the path to the key file
+     */
+    inline const std::string& key() const noexcept
+    {
+        return key_;
+    }
 };
 
 class server {
-public:
-    using handler = std::function<void (std::shared_ptr<client>, std::string)>;
+private:
+    boost::asio::io_service& service_;
+    boost::asio::ip::tcp::acceptor acceptor_;
+    boost::asio::ssl::context context_;
 
-private:
-    boost::asio::io_service& m_service;
-    boost::asio::ip::tcp::acceptor m_acceptor;
-    boost::asio::ssl::context m_context;
-    std::unordered_set<std::shared_ptr<client>> m_clients;
-    std::unordered_map<std::string, handler> m_handlers;
+    std::unordered_set<std::shared_ptr<client>> clients_;
+    std::unordered_map<std::string, std::unique_ptr<handler>> handlers_;
 
-    database m_database;
+    mlk::server::database& database_;
 
-    void handle_auth(std::shared_ptr<client>, std::string);
+    void load();
     void start();
 
-    boost::asio::ip::tcp::endpoint endpoint(const server_settings& params) const;
+    boost::asio::ip::tcp::endpoint endpoint(const settings& params) const;
+
+protected:
+    void handle_auth(std::shared_ptr<client>, nlohmann::json);
 
 public:
-    server(boost::asio::io_service& service,
-           const server_settings& sv_params,
-           const database_settings& db_params);
+    server(boost::asio::io_service& service, mlk::server::database& db, const settings& settings);
 
-    void add_handler(std::string cmd, handler func);
+    inline mlk::server::database& database() noexcept
+    {
+        return database_;
+    }
+
+    inline const mlk::server::database& database() const noexcept
+    {
+        return database_;
+    }
 
-    void handle_disconnect(std::shared_ptr<client>);
+    /**
+     * Add network handler.
+     *
+     * If a handler is already present, it will be replaced with the new one.
+     *
+     * \pre handler != nullptr
+     * \pre !name.empty()
+     * \param handler the handler
+     */
+    void handle(std::string name, std::unique_ptr<handler> handler);
 
-    void handle_message(std::shared_ptr<client>, std::string, std::string);
+    virtual void handle_disconnect(std::shared_ptr<client>);
 
-    void handle_accept(std::shared_ptr<client>, boost::system::error_code);
+    /**
+     * Asynchronous function called once a message has been completely and
+     * successfully received from the client.
+     *
+     * The default implementation searches for a handler defined for this
+     * message.
+     */
+    virtual void handle_read(std::shared_ptr<client>, nlohmann::json message);
+
+    virtual void handle_accept(std::shared_ptr<client>, boost::system::error_code);
 };
 
 } // !server
--- a/server/main.cpp	Tue Apr 25 12:44:18 2017 +0200
+++ b/server/main.cpp	Wed Jun 07 20:44:37 2017 +0200
@@ -18,25 +18,26 @@
 
 #include <iostream>
 
+#include <malikania/server/database.hpp>
 #include <malikania/server/server.hpp>
 
 int main()
 {
-    mlk::server::server_settings sv_params;
+    mlk::server::settings sv_params{
+        3320,
+        "/home/markand/null/server.crt",
+        "/home/markand/null/server.key",
+    };
 
-    sv_params.port = 3320;
-    sv_params.certificate = "/home/markand/null/server.crt";
-    sv_params.key = "/home/markand/null/server.key";
-
-    mlk::server::database_settings db_params;
-
-    db_params["type"] = "sqlite";
-    db_params["path"] = "/home/markand/kingdom.db";
+    mlk::server::database db({
+        { "type", "sqlite" },
+        { "path", "/home/markand/kingdom.db" }
+    });
 
     boost::asio::io_service service;
 
     try {
-        mlk::server::server server(service, sv_params, db_params);
+        mlk::server::server server(service, db, sv_params);
 
         for (;;) {
             service.run();