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)
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)
{
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
// This is a response
info.optionState = OptInfo::ACKNOWLEDGED;
info.enabled = enabled;
+ decrementCount = true;
}
else if (enabled != info.enabled) {
// Request to change the current state
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();
+ }
+ }
}
///////////////////////////////////////////////////////////////////////////
{
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////////////////////////////////////////
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);
#include <map>
#include <senf/Socket.hh>
#include <senf/Scheduler/Scheduler.hh>
+#include <senf/Scheduler/ClockService.hh>
//#include "Telnet.mpp"
///////////////////////////////hh.p////////////////////////////////////////
class BaseTelnetProtocol
{
public:
+ static unsigned const DEFAULT_REQUEST_TIMEOUT_MS = 500u;
+
typedef ClientSocketHandle<senf::MakeSocketPolicy<
ConnectedCommunicationPolicy,
StreamFramingPolicy,
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;
void readHandler(int state);
void writeHandler(int state);
+ void timeout();
enum Command {
CMD_NONE = 0,
senf::scheduler::FdEvent inputEvent_;
senf::scheduler::FdEvent outputEvent_;
+ unsigned pendingRequests_;
+
+ ClockService::clock_type requestTimeout_;
+ scheduler::TimerEvent timeout_;
+
friend class 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
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_;
};
}
//#include "telnetServer.ih"
// Custom includes
+#include <boost/bind.hpp>
#include "Telnet.hh"
+#include "../../Scheduler/Scheduler.hh"
#include "../Logger.hh"
#include "../../Socket/Protocols/INet.hh"
{
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();