libt2n: (gerd) client callbacks
authorGerd v. Egidy <gerd.von.egidy@intra2net.com>
Sat, 28 Oct 2006 00:57:25 +0000 (00:57 +0000)
committerGerd v. Egidy <gerd.von.egidy@intra2net.com>
Sat, 28 Oct 2006 00:57:25 +0000 (00:57 +0000)
src/client.cpp
src/client.hxx
src/socket_client.cpp
test/callback.cpp

index 2ce213b..96b2554 100644 (file)
@@ -25,11 +25,49 @@ namespace libt2n
 {
 
 client_connection::client_connection()
-    : connection()
+    : connection(), callbacks(__events_end)
 {
     set_logging(NULL,none);
 }
 
+client_connection::~client_connection()
+{
+    // we want the connection_closed callbacks to be called before
+    close();
+
+    do_callbacks(connection_deleted);
+}
+
+void client_connection::close()
+{
+    if (!is_closed())
+    {
+        connection::close();
+        do_callbacks(connection_closed);
+    }
+}
+
+/** @brief add a callback
+
+    @param event event the function will be called at
+    @param func functor (see boost function) that will be called
+
+    @example use boost::bind to bind to member functions and parameters like this:
+        int this example 17 is a fixed parameter that is always added to the call
+        c.add_callback(connection_closed,bind(&my_class::func_to_call_back, boost::ref(*this), 17));
+*/
+void client_connection::add_callback(callback_event_type event, const boost::function<void ()>& func)
+{
+    callbacks[event].push_back(func);
+}
+
+void client_connection::do_callbacks(callback_event_type event)
+{
+    std::list<boost::function<void ()> >::iterator i,ie=callbacks[event].end();
+    for (i=callbacks[event].begin(); i != ie; i++)
+        (*i)();
+}
+
 /// get pointer to logging stream, returns NULL if no logging needed
 std::ostream* client_connection::get_logstream(log_level_values level)
 {
index 91c17b9..c639bb1 100644 (file)
 #define __LIBT2N_CLIENT
 
 #include <string>
+#include <vector>
+#include <list>
+
+#include <boost/function.hpp>
 
 #include "connection.hxx"
 #include "types.hxx"
@@ -35,12 +39,24 @@ class client_connection : public connection
         log_level_values log_level;
         std::ostream *logstream;
 
+        /// vector initialized for all callback-types, all elements in each list will be called
+        std::vector<std::list<boost::function<void ()> > > callbacks;
+
+    protected:
+        void do_callbacks(callback_event_type event);
+
     public:
         client_connection();
 
+        virtual ~client_connection();
+
+        void close();
+
         void set_logging(std::ostream *_logstream, log_level_values _log_level);
 
         std::ostream* get_logstream(log_level_values level);
+
+        void add_callback(callback_event_type event, const boost::function<void ()>& func);
 };
 
 }
index 7406f28..067b033 100644 (file)
@@ -109,6 +109,8 @@ void socket_client_connection::connect()
         throw t2n_connect_error(string("invalid connection type"));
 
     set_socket_options(sock);
+
+    do_callbacks(new_connection);
 }
 
 void socket_client_connection::close()
index beb0c77..9d4c7d1 100644 (file)
@@ -33,10 +33,12 @@ class test_callback : public TestFixture
 {
     CPPUNIT_TEST_SUITE(test_callback);
 
-    CPPUNIT_TEST(NewConnCallback);
-    CPPUNIT_TEST(ConnClosedCallback);
-    CPPUNIT_TEST(ConnDeletedCallback);
-    CPPUNIT_TEST(CallbackOrder);
+    CPPUNIT_TEST(ServerNewConnCallback);
+    CPPUNIT_TEST(ServerConnClosedCallback);
+    CPPUNIT_TEST(ServerConnDeletedCallback);
+    CPPUNIT_TEST(ServerCallbackOrder);
+    CPPUNIT_TEST(ClientConnClosedCallback);
+    CPPUNIT_TEST(ClientConnDeletedCallback);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -63,7 +65,7 @@ class test_callback : public TestFixture
         callback_done[ev]=true;
     }
 
-    void NewConnCallback()
+    void ServerNewConnCallback()
     {
         pid_t pid;
 
@@ -124,7 +126,7 @@ class test_callback : public TestFixture
         }
     }
 
-    void ConnClosedCallback()
+    void ServerConnClosedCallback()
     {
         pid_t pid;
 
@@ -185,7 +187,7 @@ class test_callback : public TestFixture
         }
     }
 
-    void ConnDeletedCallback()
+    void ServerConnDeletedCallback()
     {
         pid_t pid;
 
@@ -249,7 +251,7 @@ class test_callback : public TestFixture
         }
     }
 
-    void CallbackOrder()
+    void ServerCallbackOrder()
     {
         pid_t pid;
 
@@ -332,6 +334,115 @@ class test_callback : public TestFixture
         }
     }
 
+    void ClientConnClosedCallback()
+    {
+        pid_t pid;
+
+        switch(pid=fork())
+        {
+            case -1:
+            {
+                CPPUNIT_FAIL("fork error");
+                break;
+            }
+            case 0:
+            // child
+            {
+                socket_server ss("./socket");
+
+                // max 3 sec
+                for (int i=0; i < 3; i++)
+                {
+                    ss.fill_buffer(1000000);
+
+                    string data;
+                    unsigned int cid;
+                    if(ss.get_packet(data,cid))
+                        break;
+                }
+                // don't call atexit and stuff
+                _exit(0);
+            }
+
+            default:
+            // parent
+            {
+                string data;
+                // wait till server is up
+                sleep(1);
+
+                socket_client_connection sc("./socket");
+
+                sc.add_callback(connection_closed,bind(&test_callback::callback_func, boost::ref(*this), connection_closed, 0));
+
+                sc.write("ABC");
+
+                // wait half a sec
+                sc.fill_buffer(500000);
+                sc.get_packet(data);
+
+                CPPUNIT_ASSERT_EQUAL(false,static_cast<bool>(callback_done[new_connection]));
+                CPPUNIT_ASSERT_EQUAL(true,static_cast<bool>(callback_done[connection_closed]));
+                CPPUNIT_ASSERT_EQUAL(false,static_cast<bool>(callback_done[connection_deleted]));
+            }
+        }
+    }
+
+    void ClientConnDeletedCallback()
+    {
+        pid_t pid;
+
+        switch(pid=fork())
+        {
+            case -1:
+            {
+                CPPUNIT_FAIL("fork error");
+                break;
+            }
+            case 0:
+            // child
+            {
+                socket_server ss("./socket");
+
+                // max 3 sec
+                for (int i=0; i < 3; i++)
+                {
+                    ss.fill_buffer(1000000);
+
+                    string data;
+                    unsigned int cid;
+                    if(ss.get_packet(data,cid))
+                        break;
+                }
+                // don't call atexit and stuff
+                _exit(0);
+            }
+
+            default:
+            // parent
+            {
+                string data;
+                // wait till server is up
+                sleep(1);
+
+                {
+                    socket_client_connection sc("./socket");
+
+                    sc.add_callback(connection_deleted,bind(&test_callback::callback_func, boost::ref(*this), connection_deleted, 0));
+
+                    sc.write("ABC");
+
+                    // wait half a sec
+                    sc.fill_buffer(500000);
+                    sc.get_packet(data);
+                }
+
+                CPPUNIT_ASSERT_EQUAL(false,static_cast<bool>(callback_done[new_connection]));
+                CPPUNIT_ASSERT_EQUAL(false,static_cast<bool>(callback_done[connection_closed]));
+                CPPUNIT_ASSERT_EQUAL(true,static_cast<bool>(callback_done[connection_deleted]));
+            }
+        }
+    }
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(test_callback);