Utils/Console: Implement v_setupComplete() callback and request timeout
g0dil [Fri, 2 Jan 2009 14:21:29 +0000 (14:21 +0000)]
git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1034 270642c3-0616-0410-b53a-bc976706d245

Utils/Console/Telnet.cc
Utils/Console/Telnet.cci
Utils/Console/Telnet.hh
Utils/Console/telnetServer.cc

index bc4b515..c99efc3 100644 (file)
@@ -41,13 +41,20 @@ prefix_ senf::console::detail::BaseTelnetProtocol::BaseTelnetProtocol(Handle han
                    senf::scheduler::FdEvent::EV_READ),
       outputEvent_ ("senf::console::detail::BaseTelnetProtocol::output",
                     senf::membind(&BaseTelnetProtocol::writeHandler, this), handle,
-                    senf::scheduler::FdEvent::EV_WRITE, false)
+                    senf::scheduler::FdEvent::EV_WRITE, false),
+      pendingRequests_ (0u),
+      requestTimeout_ (ClockService::milliseconds(DEFAULT_REQUEST_TIMEOUT_MS)),
+      timeout_ ("senf::console::detail::BaseTelnetProtocol::timeout",
+                senf::membind(&BaseTelnetProtocol::timeout, this))
 {}
 
 prefix_ senf::console::detail::BaseTelnetProtocol::BaseTelnetProtocol()
     : handle_ (), charState_ (NORMAL), command_ (CMD_NONE), option_ (0),
       inputEvent_ ("senf::console::detail::BaseTelnetProtocol::input", 0),
-      outputEvent_ ("senf::console::detail::BaseTelnetProtocol::output", 0)
+      outputEvent_ ("senf::console::detail::BaseTelnetProtocol::output", 0),
+      pendingRequests_ (0u),
+      requestTimeout_ (ClockService::milliseconds(DEFAULT_REQUEST_TIMEOUT_MS)),
+      timeout_ ("senf::console::detail::BaseTelnetProtocol::timeout", 0)
 {}
 
 prefix_ void senf::console::detail::BaseTelnetProtocol::write(std::string const & s)
@@ -346,6 +353,14 @@ prefix_ void senf::console::detail::BaseTelnetProtocol::writeHandler(int state)
         outputEvent_.disable();
 }
 
+prefix_ void senf::console::detail::BaseTelnetProtocol::timeout()
+{
+    if (pendingRequests_ > 0u) {
+        pendingRequests_ = 0u;
+        v_setupComplete();
+    }
+}
+
 prefix_ senf::console::detail::BaseTelnetProtocol::OptInfo &
 senf::console::detail::BaseTelnetProtocol::getOption(bool local, option_type option)
 {
@@ -364,11 +379,14 @@ prefix_ void senf::console::detail::BaseTelnetProtocol::request(OptInfo & info,
         transmit((info.local ? CMD_WILL : CMD_DO) + (enabled ? 0 : 1));
         transmit(info.option);
         info.optionState = OptInfo::REQUEST_SENT;
+        incrementRequestCounter();
     }
 }
 
 prefix_ void senf::console::detail::BaseTelnetProtocol::response(OptInfo & info, bool enabled)
 {
+    bool decrementCount (false);
+
     // If this is a response, we need to unconditionally accept it.  If this is a remote
     // configuration request, we accept it if wantState is wither WANTED or ACCEPTED.  If this is a
     // response, we never send out a reply. If it is a remote request we send out a reply only if
@@ -377,6 +395,7 @@ prefix_ void senf::console::detail::BaseTelnetProtocol::response(OptInfo & info,
         // This is a response
         info.optionState = OptInfo::ACKNOWLEDGED;
         info.enabled = enabled;
+        decrementCount = true;
     }
     else if (enabled != info.enabled) {
         // Request to change the current state
@@ -401,6 +420,21 @@ prefix_ void senf::console::detail::BaseTelnetProtocol::response(OptInfo & info,
         if (i != handlers_.end())
             i->second->v_init();
     }
+    if (decrementCount)
+        // This call must be AFTER calling v_init since v_init might increment the request count
+        // again. Otherwise v_setupComplete might be called prematurely
+        decrementRequestCounter();
+}
+
+prefix_ void senf::console::detail::BaseTelnetProtocol::decrementRequestCounter()
+{
+    if (pendingRequests_ > 0u) {
+        -- pendingRequests_;
+        if (pendingRequests_ == 0u) {
+            timeout_.disable();
+            v_setupComplete();
+        }
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -421,34 +455,42 @@ v_handleOptionParameters(std::string const & data)
 {
     if (data.size() <= 0)
         return;
-    if (data[0] == '\x00')
-        v_handleTerminalType(data.substr(1));
+    if (data[0] == '\x00') {
+        type_ = data.substr(1);
+        decrementRequestCounter();
+    }
 }
 
 prefix_ void senf::console::detail::telnethandler::TerminalType::v_init()
 {
     nextTerminalType();
+    incrementRequestCounter();
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::detail::telnethandler::NAWS
 
 prefix_ senf::console::detail::telnethandler::NAWS::NAWS()
+    : width_ (0u), height_ (0u)
 {
     registerHandler(this);
 }
 
 prefix_ void senf::console::detail::telnethandler::NAWS::v_init()
-{}
+{
+    incrementRequestCounter();
+}
 
 prefix_ void
 senf::console::detail::telnethandler::NAWS::v_handleOptionParameters(std::string const & data)
 {
     if (data.size() != 4)
         return;
-    v_handleWindowSize(
-        (static_cast<unsigned char>(data[0])<<8)+static_cast<unsigned char>(data[1]),
-        (static_cast<unsigned char>(data[2])<<8)+static_cast<unsigned char>(data[3]));
+    width_ = (static_cast<unsigned char>(data[0])<<8)+static_cast<unsigned char>(data[1]);
+    height_ = (static_cast<unsigned char>(data[2])<<8)+static_cast<unsigned char>(data[3]);
+    if (! requestsPending())
+        v_windowSizeChanged();
+    decrementRequestCounter();
 }
 
 ///////////////////////////////cc.e////////////////////////////////////////
index 178a559..7dc6f2a 100644 (file)
@@ -47,12 +47,44 @@ prefix_ senf::console::detail::BaseTelnetProtocol::OptInfo::OptInfo(bool l, opti
 prefix_ senf::console::detail::BaseTelnetProtocol::TelnetHandler::~TelnetHandler()
 {}
 
+prefix_ std::string const & senf::console::detail::telnethandler::TerminalType::terminalType()
+    const
+{
+    return type_;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::telnethandler::NAWS
+
+prefix_ unsigned senf::console::detail::telnethandler::NAWS::width()
+    const
+{
+    return width_;
+}
+
+prefix_ unsigned senf::console::detail::telnethandler::NAWS::height()
+    const
+{
+    return height_;
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::detail::BaseTelnetProtocol
 
 prefix_ senf::console::detail::BaseTelnetProtocol::~BaseTelnetProtocol()
 {}
 
+prefix_ void senf::console::detail::BaseTelnetProtocol::incrementRequestCounter()
+{
+    ++ pendingRequests_;
+    timeout_.timeout(senf::scheduler::eventTime() + requestTimeout_);
+}
+
+prefix_ bool senf::console::detail::BaseTelnetProtocol::requestsPending()
+{
+    return pendingRequests_ > 0u;
+}
+
 prefix_ void senf::console::detail::BaseTelnetProtocol::sendNOP()
 {
     transmit(CMD_IAC);
index ee952b8..d813474 100644 (file)
@@ -31,6 +31,7 @@
 #include <map>
 #include <senf/Socket.hh>
 #include <senf/Scheduler/Scheduler.hh>
+#include <senf/Scheduler/ClockService.hh>
 
 //#include "Telnet.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
@@ -50,6 +51,8 @@ namespace detail {
     class BaseTelnetProtocol
     {
     public:
+        static unsigned const DEFAULT_REQUEST_TIMEOUT_MS = 500u;
+
         typedef ClientSocketHandle<senf::MakeSocketPolicy<
             ConnectedCommunicationPolicy, 
             StreamFramingPolicy, 
@@ -88,11 +91,16 @@ namespace detail {
         template <class Handler>
         void registerHandler(Handler * h, bool request=true);
 
+        void incrementRequestCounter();
+        void decrementRequestCounter();
+        bool requestsPending();
+
     private:
 
 #ifndef DOXYGEN
     private:
 #endif
+        virtual void v_setupComplete() = 0;
         virtual void v_charReceived(char c) = 0;
         virtual void v_eof() = 0;
 
@@ -127,6 +135,7 @@ namespace detail {
 
         void readHandler(int state);
         void writeHandler(int state);
+        void timeout();
 
         enum Command { 
             CMD_NONE = 0,
@@ -193,6 +202,11 @@ namespace detail {
         senf::scheduler::FdEvent inputEvent_;
         senf::scheduler::FdEvent outputEvent_;
 
+        unsigned pendingRequests_;
+
+        ClockService::clock_type requestTimeout_;
+        scheduler::TimerEvent timeout_;
+
         friend class TelnetHandler;
     };
 
@@ -221,15 +235,16 @@ namespace telnethandler {
         static option_type const OPTION_CODE = telnetopt::TERMINAL_TYPE;
 
         void nextTerminalType();
+        std::string const & terminalType() const;
         
     protected:
         TerminalType();
 
     private:
-        virtual void v_handleTerminalType(std::string const & type) = 0;
-
         virtual void v_init();
         virtual void v_handleOptionParameters(std::string const & data);
+
+        std::string type_;
     };
 
     class NAWS 
@@ -238,14 +253,20 @@ namespace telnethandler {
     public:
         static option_type const OPTION_CODE = telnetopt::NAWS;
 
+        unsigned width() const;
+        unsigned height() const;
+
     protected:
         NAWS();
         
     private:
-        virtual void v_handleWindowSize(unsigned width, unsigned height) = 0;
+        virtual void v_windowSizeChanged() = 0;
 
         virtual void v_init();
         virtual void v_handleOptionParameters(std::string const & data);
+
+        unsigned width_;
+        unsigned height_;
     };
     
 }
index d70a084..95f290c 100644 (file)
@@ -27,7 +27,9 @@
 //#include "telnetServer.ih"
 
 // Custom includes
+#include <boost/bind.hpp>
 #include "Telnet.hh"
+#include "../../Scheduler/Scheduler.hh"
 #include "../Logger.hh"
 #include "../../Socket/Protocols/INet.hh"
 
@@ -55,34 +57,49 @@ namespace {
             {
                 SENF_LOG(("Char: " << c));
             }
+
         virtual void v_eof()
             {
                 SENF_LOG(("EOF"));
-                senf::scheduler::terminate();
+                delete this;
             }
-        virtual void v_handleTerminalType(std::string const & type)
+
+        virtual void v_setupComplete()
             {
-                SENF_LOG(("Terminal type: " << type));
+                SENF_LOG(("Terminal type is '" << terminalType() << "', window size is "
+                          << width() << "x" << height()));
             }
 
-        virtual void v_handleWindowSize(unsigned width, unsigned height)
+        virtual void v_windowSizeChanged()
             {
-                SENF_LOG(("Window size: " << width << "x" << height));
+                SENF_LOG(("New window size: " << width() << "x" << height()));
             }
     };
 
     typedef senf::TCPv4ServerSocketHandle ServerHandle;
     typedef ServerHandle::ClientHandle ClientHandle;
-    
+
+    void connect(ServerHandle handle, int events)
+    {
+        if (events != senf::scheduler::FdEvent::EV_READ) {
+            senf::scheduler::terminate();
+            return;
+        }
+        ClientHandle client (handle.accept());
+        SENF_LOG(("new client ..."));
+        new MyTelnet (client);
+    }
+
 }
 
 int main(int argc, char const ** argv)
 {
-    SENF_LOG(("Starting server."));
+    senf::log::ConsoleTarget::instance().timeFormat("");
+
     ServerHandle server (ServerHandle::Address("127.0.0.1:22344"));
-    ClientHandle client (server.accept());
-    SENF_LOG(("Starting MyTelnet"));
-    MyTelnet telnet (client);
+    senf::scheduler::FdEvent serverEvent ("telnetServer", boost::bind(&connect, server, _1),
+                                          server, senf::scheduler::FdEvent::EV_READ);
+    SENF_LOG(("Server started at " << server.local()));
 
     senf::scheduler::process();