From 922cb5559b9f2f97279fa24cc9c5862c8b666495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 8 Mar 2005 10:23:43 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r2603, which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/iem/iemxmlrpc/; revision=2604 --- xmlrpc++/src/XmlRpcServer.cpp | 284 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 xmlrpc++/src/XmlRpcServer.cpp (limited to 'xmlrpc++/src/XmlRpcServer.cpp') diff --git a/xmlrpc++/src/XmlRpcServer.cpp b/xmlrpc++/src/XmlRpcServer.cpp new file mode 100644 index 0000000..f6b4aa5 --- /dev/null +++ b/xmlrpc++/src/XmlRpcServer.cpp @@ -0,0 +1,284 @@ + +#include "XmlRpcServer.h" +#include "XmlRpcServerConnection.h" +#include "XmlRpcServerMethod.h" +#include "XmlRpcSocket.h" +#include "XmlRpcUtil.h" +#include "XmlRpcException.h" + + +using namespace XmlRpc; + + +XmlRpcServer::XmlRpcServer() +{ + _introspectionEnabled = false; + _listMethods = 0; + _methodHelp = 0; +} + + +XmlRpcServer::~XmlRpcServer() +{ + this->shutdown(); + _methods.clear(); + delete _listMethods; + delete _methodHelp; +} + + +// Add a command to the RPC server +void +XmlRpcServer::addMethod(XmlRpcServerMethod* method) +{ + _methods[method->name()] = method; +} + +// Remove a command from the RPC server +void +XmlRpcServer::removeMethod(XmlRpcServerMethod* method) +{ + MethodMap::iterator i = _methods.find(method->name()); + if (i != _methods.end()) + _methods.erase(i); +} + +// Remove a command from the RPC server by name +void +XmlRpcServer::removeMethod(const std::string& methodName) +{ + MethodMap::iterator i = _methods.find(methodName); + if (i != _methods.end()) + _methods.erase(i); +} + + +// Look up a method by name +XmlRpcServerMethod* +XmlRpcServer::findMethod(const std::string& name) const +{ + MethodMap::const_iterator i = _methods.find(name); + if (i == _methods.end()) + return 0; + return i->second; +} + + +// Create a socket, bind to the specified port, and +// set it in listen mode to make it available for clients. +bool +XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/) +{ + int fd = XmlRpcSocket::socket(); + if (fd < 0) + { + XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + this->setfd(fd); + + // Don't block on reads/writes + if ( ! XmlRpcSocket::setNonBlocking(fd)) + { + this->close(); + XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + // Allow this port to be re-bound immediately so server re-starts are not delayed + if ( ! XmlRpcSocket::setReuseAddr(fd)) + { + this->close(); + XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + // Bind to the specified port on the default interface + if ( ! XmlRpcSocket::bind(fd, port)) + { + this->close(); + XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + // Set in listening mode + if ( ! XmlRpcSocket::listen(fd, backlog)) + { + this->close(); + XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); + return false; + } + + XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", port, fd); + + // Notify the dispatcher to listen on this source when we are in work() + _disp.addSource(this, XmlRpcDispatch::ReadableEvent); + + return true; +} + + +// Process client requests for the specified time +void +XmlRpcServer::work(double msTime) +{ + XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection"); + _disp.work(msTime); +} + + + +// Handle input on the server socket by accepting the connection +// and reading the rpc request. +unsigned +XmlRpcServer::handleEvent(unsigned mask) +{ + acceptConnection(); + return XmlRpcDispatch::ReadableEvent; // Continue to monitor this fd +} + + +// Accept a client connection request and create a connection to +// handle method calls from the client. +void +XmlRpcServer::acceptConnection() +{ + int s = XmlRpcSocket::accept(this->getfd()); + XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s); + if (s < 0) + { + //this->close(); + XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str()); + } + else if ( ! XmlRpcSocket::setNonBlocking(s)) + { + XmlRpcSocket::close(s); + XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); + } + else // Notify the dispatcher to listen for input on this source when we are in work() + { + XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection"); + _disp.addSource(this->createConnection(s), XmlRpcDispatch::ReadableEvent); + } +} + + +// Create a new connection object for processing requests from a specific client. +XmlRpcServerConnection* +XmlRpcServer::createConnection(int s) +{ + // Specify that the connection object be deleted when it is closed + return new XmlRpcServerConnection(s, this, true); +} + + +void +XmlRpcServer::removeConnection(XmlRpcServerConnection* sc) +{ + _disp.removeSource(sc); +} + + +// Stop processing client requests +void +XmlRpcServer::exit() +{ + _disp.exit(); +} + + +// Close the server socket file descriptor and stop monitoring connections +void +XmlRpcServer::shutdown() +{ + // This closes and destroys all connections as well as closing this socket + _disp.clear(); +} + + +// Introspection support +static const std::string LIST_METHODS("system.listMethods"); +static const std::string METHOD_HELP("system.methodHelp"); +static const std::string MULTICALL("system.multicall"); + + +// List all methods available on a server +class ListMethods : public XmlRpcServerMethod +{ +public: + ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {} + + void execute(XmlRpcValue& params, XmlRpcValue& result) + { + _server->listMethods(result); + } + + std::string help() { return std::string("List all methods available on a server as an array of strings"); } +}; + + +// Retrieve the help string for a named method +class MethodHelp : public XmlRpcServerMethod +{ +public: + MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {} + + void execute(XmlRpcValue& params, XmlRpcValue& result) + { + if (params[0].getType() != XmlRpcValue::TypeString) + throw XmlRpcException(METHOD_HELP + ": Invalid argument type"); + + XmlRpcServerMethod* m = _server->findMethod(params[0]); + if ( ! m) + throw XmlRpcException(METHOD_HELP + ": Unknown method name"); + + result = m->help(); + } + + std::string help() { return std::string("Retrieve the help string for a named method"); } +}; + + +// Specify whether introspection is enabled or not. Default is enabled. +void +XmlRpcServer::enableIntrospection(bool enabled) +{ + if (_introspectionEnabled == enabled) + return; + + _introspectionEnabled = enabled; + + if (enabled) + { + if ( ! _listMethods) + { + _listMethods = new ListMethods(this); + _methodHelp = new MethodHelp(this); + } else { + addMethod(_listMethods); + addMethod(_methodHelp); + } + } + else + { + removeMethod(LIST_METHODS); + removeMethod(METHOD_HELP); + } +} + + +void +XmlRpcServer::listMethods(XmlRpcValue& result) +{ + int i = 0; + result.setSize(_methods.size()+1); + for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it) + result[i++] = it->first; + + // Multicall support is built into XmlRpcServerConnection + result[i] = MULTICALL; +} + + + -- cgit v1.2.1