changeset 147:ba8642323700

Client: use handlers instead
author David Demelier <markand@malikania.fr>
date Thu, 26 Oct 2017 12:28:33 +0200
parents 91e57baa2ede
children e21942f73ef6
files libclient/malikania/client/connection.cpp libclient/malikania/client/connection.hpp
diffstat 2 files changed, 101 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/libclient/malikania/client/connection.cpp	Sat Sep 30 13:35:45 2017 +0200
+++ b/libclient/malikania/client/connection.cpp	Thu Oct 26 12:28:33 2017 +0200
@@ -16,111 +16,115 @@
  * 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
+ * ------------------------------------------------------------------
+ */
 connection::connection(boost::asio::io_service& service)
     : service_(service)
     , context_(boost::asio::ssl::context::sslv23)
+    , resolver_(service_)
     , socket_(service_, context_)
 {
 }
 
-void connection::handshake(client& client)
+/*
+ * connection::handshake
+ * ------------------------------------------------------------------
+ */
+void connection::handshake(connect_t handler)
 {
-#if 0
-    socket_.async_handshake(boost::asio::ssl::stream_base::client, [this, &client] (auto code) {
-        if (code) {
-            client.handle_error(code);
-        } else {
-            client.handle_connect(code);
-        }
-    });
-#endif
+    socket_.async_handshake(boost::asio::ssl::stream_base::client, std::move(handler));
 }
 
-void connection::do_connect(client& client, std::string host, std::uint16_t port)
+/*
+ * connection::flush
+ * ------------------------------------------------------------------
+ */
+void connection::flush()
 {
-#if 0
+    if (output_.empty())
+        return;
+
+    auto buffer = boost::asio::buffer(std::get<1>(output_[0]));
+
+    boost::asio::async_write(socket_, buffer, [this] (auto code, auto xfer) {
+        auto& handler = std::get<2>(output_[0]);
+
+        if (handler)
+            handler(std::move(code), std::get<0>(output_[0]));
+
+        if (!code && xfer != 0)
+            flush();
+    });
+}
+
+/*
+ * connection::connect
+ * ------------------------------------------------------------------
+ */
+void connection::connect(const std::string& host, std::uint16_t port, connect_t handler)
+{
     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 {
-                    this->handshake(client);
-                }
+    resolver_.async_resolve(tcp::resolver::query(host, str), [this, handler] (auto code, auto ep) {
+        if (code)
+            handler(std::move(code));
+        else {
+            boost::asio::async_connect(socket_.lowest_layer(), ep, [this, handler] (auto code, auto) {
+                if (code)
+                    handler(std::move(code));
+                else
+                    handshake(std::move(handler));
             });
         }
     });
-#endif
 }
 
-void connection::flush(client& client)
+/*
+ * connection::send
+ * ------------------------------------------------------------------
+ */
+void connection::send(nlohmann::json message, send_t handler)
 {
-#if 0
-    auto buffer = boost::asio::buffer(output_.front().data(), output_.front().length());
-
-    boost::asio::async_write(socket_, buffer, [this, &client] (auto code, auto xfer) {
-        output_.pop_front();
-
-        if (xfer == 0) {
-            client.handle_disconnect();
-        } else if (code) {
-            client.handle_error(code);
-        } else if (!output_.empty()) {
-            this->flush(client);
-        }
-    });
-#endif
-}
-
-void connection::do_send(client& client, nlohmann::json message)
-{
-#if 0
     assert(message.is_object());
 
     auto in_progress = !output_.empty();
-
-    output_.push_back(message.dump() + "\r\n\r\n");
+    auto str = message.dump() + "\r\n\r\n";
 
-    if (!in_progress) {
-        flush(client);
-    }
-#endif
+    output_.push_back(std::make_tuple(std::move(message), std::move(str), std::move(handler)));
+
+    if (!in_progress)
+        flush();
 }
 
-void connection::do_read(client& client)
+/*
+ * connection::recv
+ * ------------------------------------------------------------------
+ */
+void connection::read(recv_t handler)
 {
-#if 0
 #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) {
+    boost::asio::async_read_until(socket_, input_, "\r\n\r\n", [this, handler] (auto code, auto xfer) {
 #if !defined(NDEBUG)
         is_reading = false;
 #endif
-        if (xfer == 0) {
-            client.handle_disconnect();
-        } else if (code) {
-            client.handle_error(code);
-        } else {
+        if (code || xfer == 0)
+            handler(std::move(code), nullptr);
+        else {
             std::string command{
                 boost::asio::buffers_begin(input_.data()),
                 boost::asio::buffers_begin(input_.data()) + xfer - /* \r\n\r\n */ 4
@@ -129,13 +133,12 @@
             input_.consume(xfer);
 
             try {
-                client.handle_read(std::move(code), nlohmann::json::parse(command));
-            } catch (const std::exception& ex) {
-                std::cerr << "connection::do_read: " << ex.what() << std::endl;
+                handler(std::move(code), nlohmann::json::parse(command));
+            } catch (const std::exception&) {
+                // TODO: add custom error code.
             }
         }
     });
-#endif
 }
 
 } // !client
--- a/libclient/malikania/client/connection.hpp	Sat Sep 30 13:35:45 2017 +0200
+++ b/libclient/malikania/client/connection.hpp	Thu Oct 26 12:28:33 2017 +0200
@@ -25,6 +25,8 @@
  */
 
 #include <deque>
+#include <functional>
+#include <utility>
 
 #include <boost/asio.hpp>
 #include <boost/asio/ssl.hpp>
@@ -42,19 +44,40 @@
  */
 class connection {
 public:
+    /**
+     * Connection handler.
+     */
+    using connect_t = std::function<void (boost::system::error_code)>;
+
+    /**
+     * Receive handler.
+     */
+    using recv_t = std::function<void (boost::system::error_code, nlohmann::json)>;
+
+    /**
+     * Send handler.
+     */
+    using send_t = std::function<void (boost::system::error_code, nlohmann::json)>;
+
 private:
+    /**
+     * Wrap an handler, the JSON message and its dump string.
+     */
+    using output_item_t = std::tuple<nlohmann::json, std::string, send_t>;
+
     boost::asio::io_service& service_;
     boost::asio::ssl::context context_;
+    boost::asio::ip::tcp::resolver resolver_;
     boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
     boost::asio::streambuf input_;
-    std::deque<std::string> output_;
+    std::deque<output_item_t> output_;
 
 #if !defined(NDEBUG)
     bool is_reading{false};
 #endif
 
-    void handshake(client&);
-    void flush(client&);
+    void handshake(connect_t);
+    void flush();
 
 public:
     /**
@@ -70,23 +93,25 @@
      * \param port the port number
      * \param handler the handler
      */
-    virtual void do_connect(client& client, std::string host, std::uint16_t port);
+    virtual void connect(const std::string& host, std::uint16_t port, connect_t handler);
 
     /**
      * Send the given message to the server.
      *
      * \pre message.is_object()
-     * \pre handler != nullptr
      * \param message the message object
+     * \param handler the handler (may be null)
      */
-    virtual void do_send(client& client, nlohmann::json message);
+    virtual void send(nlohmann::json message, send_t handler);
 
     /**
      * Request for a read operation.
      *
      * \pre no reading operation must be pending
+     * \pre handler != nullptr
+     * \param handler the handler
      */
-    virtual void do_read(client& client);
+    virtual void read(recv_t handler);
 };
 
 } // !client