This class provides an interactive console TCP server.
- \todo Add readline support
\todo Add interactivity detection using timeout
+ \idea To support blocking commands, we could give the Client 'suspend()' and 'resume()'
+ members. suspend() would probably throw some kind of exception to transfer control back
+ to the Client instance. on resume(), the command would be called again, maybe setting
+ some flag or something. Example for use: Host name resolution: Here we can just built
+ our own little host-name cache. When the name is not found, we ask the resolver to
+ resolve it and call 'resume' when the name is found. Since it is in the cache now, the
+ command will now complete.
+
+ \implementation We do \e not provide an \c instance() member so we can easily later extend
+ the server to allow registering more than one instance, e.g. with each instance on a
+ differently firewalled port and with different security restrictions.
*/
class Server
: boost::noncopyable
public:
///////////////////////////////////////////////////////////////////////////
// Types
-
- typedef senf::ServerSocketHandle<
- senf::MakeSocketPolicy< senf::TCPv4SocketProtocol::Policy,
- senf::UnspecifiedAddressingPolicy>::policy > ServerHandle;
+
+ typedef detail::ServerHandle ServerHandle;
~Server();
///< Start server on given IPv4 address/port
static Server & start(senf::INet6SocketAddress const & address);
///< Start server on given IPv6 address/port
-
- void name(std::string const & name); ///< Set server name
+ Server & name(std::string const & name); ///< Set server name
/**< This information is used in the prompt string. */
-
+
+ void stop(); ///< Stop the server
+ /**< All clients will be closed */
+
protected:
private:
Server(ServerHandle handle);
- static void start(ServerHandle handle);
+ static Server & start(ServerHandle handle);
+ static boost::scoped_ptr<Server> & instancePtr();
void newClient(Scheduler::EventId event);
void removeClient(Client & client);
Clients clients_;
std::string name_;
- static boost::scoped_ptr<Server> instance_;
-
friend class Client;
};
Whenever a new client connects, a new instance of this class is created. This class shows a
command prompt, receives the commands, parses them and then passes (using a CommandParser)
and passes the commands to an Executor instance.
-
- \fixme Fix Client::clientData implementation
- \fixme Don't register a new ReadHelper every round
- \fixme Ensure, that output errors (or any errors) in the console don't terminate the
- application
*/
class Client
: public senf::intrusive_refcount,
SENF_LOG_CLASS_AREA();
SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE );
+
public:
typedef Server::ServerHandle::ClientSocketHandle ClientHandle;
~Client();
- void stopClient(); ///< Stop the client
+ void stop(); ///< Stop the client
/**< This will close the client socket. */
+ std::string const & name() const;
+ ClientHandle handle() const;
+ std::ostream & stream();
+ std::string promptString() const;
+
+ static Client & get(std::ostream & os);
+
protected:
private:
- Client(ClientHandle handle, std::string const & name);
-
- void clientData(ReadHelper<ClientHandle>::ptr helper);
- void showPrompt();
+ Client(Server & server, ClientHandle handle, std::string const & name);
+ void translate(std::string & data);
+ void handleInput(std::string input);
virtual void v_write(boost::posix_time::ptime timestamp, std::string const & stream,
std::string const & area, unsigned level,
std::string const & message);
+ Server & server_;
ClientHandle handle_;
- std::string tail_;
CommandParser parser_;
Executor executor_;
std::string name_;
- unsigned promptLen_;
std::string lastCommand_;
+ boost::scoped_ptr<detail::ClientReader> reader_;
friend class Server;
+ friend class detail::ClientReader;
+ friend class detail::NonblockingSocketSink;
};
}}