From: g0dil Date: Fri, 2 Jan 2009 14:21:29 +0000 (+0000) Subject: Utils/Console: Implement v_setupComplete() callback and request timeout X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=5746957b0da2bb228b9284e9493158c113b2bbe0;p=senf.git Utils/Console: Implement v_setupComplete() callback and request timeout git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1034 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Utils/Console/Telnet.cc b/Utils/Console/Telnet.cc index bc4b515..c99efc3 100644 --- a/Utils/Console/Telnet.cc +++ b/Utils/Console/Telnet.cc @@ -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(data[0])<<8)+static_cast(data[1]), - (static_cast(data[2])<<8)+static_cast(data[3])); + width_ = (static_cast(data[0])<<8)+static_cast(data[1]); + height_ = (static_cast(data[2])<<8)+static_cast(data[3]); + if (! requestsPending()) + v_windowSizeChanged(); + decrementRequestCounter(); } ///////////////////////////////cc.e//////////////////////////////////////// diff --git a/Utils/Console/Telnet.cci b/Utils/Console/Telnet.cci index 178a559..7dc6f2a 100644 --- a/Utils/Console/Telnet.cci +++ b/Utils/Console/Telnet.cci @@ -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); diff --git a/Utils/Console/Telnet.hh b/Utils/Console/Telnet.hh index ee952b8..d813474 100644 --- a/Utils/Console/Telnet.hh +++ b/Utils/Console/Telnet.hh @@ -31,6 +31,7 @@ #include #include #include +#include //#include "Telnet.mpp" ///////////////////////////////hh.p//////////////////////////////////////// @@ -50,6 +51,8 @@ namespace detail { class BaseTelnetProtocol { public: + static unsigned const DEFAULT_REQUEST_TIMEOUT_MS = 500u; + typedef ClientSocketHandle 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_; }; } diff --git a/Utils/Console/telnetServer.cc b/Utils/Console/telnetServer.cc index d70a084..95f290c 100644 --- a/Utils/Console/telnetServer.cc +++ b/Utils/Console/telnetServer.cc @@ -27,7 +27,9 @@ //#include "telnetServer.ih" // Custom includes +#include #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();